다형성 ( Polymorphism )
하나의 작업을 다양한 형태를 가질 수 있는 형태를 의미합니다.
자바에서는 크게 2가지의 다형성을 제공합니다. 하나는 compile-time 다형성과 runtime 다형성이 있습니다.
이러한 다형성은 부모 클래스를 자식 클래스가 상속한 상태에서, 만약 부모클래스의 참조 변수가 자식 클래스의 인스턴스를 참조할 때 발생합니다.
Compile Time 다형성
Java에서는 static method를 오버로딩하는 것을 통해 Compile Time 다형성을 제공할 수 있습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
class Main {
public static void main(String[] args) {
Child child = new Child();
child.polyMethod();
Parent ref = child;
ref.polyMethod();
}
}
class Parent {
public static void polyMethod() {
System.out.println("Static parent method");
}
}
class Child extends Parent {
public static void polyMethod() {
System.out.println("Staic child method");
}
}
|

Runtime 다형성
runtime 다형성의 다른 말로 Dynamic method dispatch 라고 부를 수 있다. Runtime 다형성에 대해 들어가기 전에 먼저 클래스의 형 변환에 대해 알아야 합니다.
위와 같은 클래스는 아래와 같은 그림 구조로 되어 있습니다. 따라서 원래 자신의 객체를 생성한다 하면 자신의 참조변수를 생성한 후 자신의 클래스와 동일한 인스턴스를 만드는 형태였습니다.

up-cast & down-cast
up-cast란 자식 클래스의 인스턴스를 부모 클래스의 참조변수에 넣을 때 일어나는 형 변환
Parent ref = new Child()
down-cast란 부모 클래스의 인스턴스를 자식 클래스의 참조변수에 넣을 때 일어나는 변환
Child ref = (Child) new Parent();
쉽게 생각하자면, up-cast는 자식은 부모를 상속하므로 자식 인스턴스가 부모로 올라간다 생각하면 되고, 반대로 down-cast는 부모가 자식으로 내려간다 생각하면 됩니다.
그렇다면 둘다 자동 형 변환이 되는건가? 아무런 문제가 없나?
up-cast의 경우 부모 클래스의 데이터와 자신의 데이터까지 있는 자식 클래스에서 부모 클래스로 형 변환시 사용할 수 없는 데이터가 존재하게 됩니다. 따라서 위에서는 ChildData를 사용할 수 없습니다. 사용할 수 있는 변수와 메서드가 적어지는 것 뿐 강제 변환이 필요 없습니다..
down-cast의 경우 부모 클래스의 데이터만 가지고 있다가 자신보다 더 많은 데이터를 가지고 있는 자식클래스로 형 변환을 하는 것이기 때문에 존재하지 않는 데이터로 강제로 형 변환을 해주어야 한다.
단, 인스턴스 자체를 변경하는 것이 아니라 참조 변수의 자료형을 변경하는 것이기 때문에 인스턴스에는 아무런 변화가 없다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
class Main {
public static void main(String[] args) {
Child child = new Child();
child.childData = 4;
System.out.println(child.childData);
Parent ref = child;
Child child2 = (Child) ref;
System.out.println(child2.childData);
child2.childMethod();
}
}
class Parent {
int parentData;
}
class Child extends Parent {
int childData;
public void childMethod() {
System.out.println("childMethod");
}
}
|

따라서 이러한 up-cast에서 가장 중요한 것이 부모 클래스의 참조변수가 어떤 자식 클래스의 인스턴스를 가르키고 있는지 이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
class Main {
public static void main(String[] args) {
Parent ref = new FirstChild();
System.out.println("First child :");
System.out.println(ref instanceof Parent);
System.out.println(ref instanceof FirstChild);
System.out.println(ref instanceof Object);
ref = new SecondChild();
System.out.println("Second child :");
System.out.println(ref instanceof Parent);
System.out.println(ref instanceof FirstChild);
System.out.println(ref instanceof Object);
ref = new ThirdChild();
System.out.println("Third child :");
System.out.println(ref instanceof Parent);
System.out.println(ref instanceof FirstChild);
System.out.println(ref instanceof Object);
}
}
class Parent {
int parentData;
}
class FirstChild extends Parent {
int childData;
public void childMethod() {
System.out.println("childMethod");
}
}
class SecondChild extends Parent {}
class ThirdChild extends Parent {}
|

상속 관계간 중복 변수나 메서드에 따른 결과
부모와 자식 클래스가 동일한 변수나 메서드를 가지고 있을때는 과연 어떻게 나올까?
아래 예제를 통해서 확인해보겠습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
class Main {
public static void main(String[] args) {
Parent ref = new Child();
ref.method();
System.out.println(ref.data);
Child ref2 = new Child();
ref2.method();
System.out.println(ref2.data);
ref = new Parent();
ref.method();
System.out.println(ref.data);
}
}
class Parent {
int data = 4;
public void method() {
System.out.println("Parent method");
}
}
class Child extends Parent {
int data = 6;
public void method() {
System.out.println("Child method");
}
}
|

먼저 부모 클래스와 자식 클래스는 같은 인스턴스 변수 data가 있습니다. 또한 method()라는 메서드가 있는데
up-cast를 한 결과 메서드는 자식의 메서드를 가져오지만 인스턴스 변수의 경우 부모의 값을 가져옵니다.
다만 이는 내부 변수를 public으로 외부에서 값을 접근한 것입니다.
class Child extends Parent {
int data = 6;
public void method() {
System.out.println(String.format("%s, %s, %s",data,this.data,super.data)); //6, 6, 4
}
}
Child 클래스의 method자체를 위와 같이 변경하여 내부에서 접근한다면,
up-cast를 통한 참조변수에서 method를 호출했을 때 6,6,4 가 나오게 됩니다.
그럼 어떻게 사용할까?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
|
class Main {
public static void main(String[] args) {
인간 나 = new 인간(new 전자공학과());
System.out.println(나.장학금);
}
}
class 과 {
static int 총과;
int 총원;
int 과목수;
static {
총과++;
}
과(int 총원) {
this.총원 = 총원;
}
}
class 전자공학과 extends 과 {
static final int 총원=100;
전자공학과() {
super(총원);
}
}
class 컴퓨터공학과 extends 과 {
static final int 총원=50;
컴퓨터공학과() {
super(총원);
}
}
class 기계공학과 extends 과 {
static final int 총원=200;
기계공학과() {
super(총원);
}
}
class 인간 {
String 이름;
int 학번;
과 입학과;
int 장학금;
인간(과 입학과) {
this.입학과 = 입학과;
장학금 = 장학금얻기();
}
int 장학금얻기() {
if (입학과 instanceof 전자공학과) {
return 1_000_000;
}
if (입학과 instanceof 컴퓨터공학과) {
return 500_000;
}
return 0;
}
}
|
입학시 초기 장학금을 받는다고 생각했을 때 과마다 다르게 장학금을 어떻게 받을까에 대해 나타낸 것입니다.
만약 여기서 과가 3개가 아니라 무수히 많다면?
과를 특정 짓기 힘들기때문에 배열로 나타내야 하기위해 자료형을 특정지어야 한다. 이를 위해 부모 클래스를 통해 up-cast로 묶어 배열을 만들 수 있다.
'JAVA > 자바스터디' 카테고리의 다른 글
인터페이스 (0) | 2022.11.17 |
---|---|
상속 - 추상클래스 (0) | 2022.11.17 |
JAVA - 제어자 (0) | 2022.11.14 |
상속 (0) | 2022.11.13 |
[자바스터디] 5. 클래스 (0) | 2022.01.06 |