Java 문법 (11) - 다형성1
Java 문법 (11) - 다형성1
다형성
다형성은 이름 그대로 “다양한 형태”, “여러 형태”를 뜻합니다. 프로그래밍에서 다형성은 한 객체가 여러 타입의 객체로 취급될 수 있는 능력을 말합니다. 보통 하나의 객체는 하나의 타입으로 고정되어 있습니다. 그런데 다형성을 사용하면 하나의 객체가 다른 타입으로 사용될 수 있습니다.
다형성을 이해하기 위해서는 다형적 참조
, 메서드 오버라이딩
2가지 핵심 이론을 알아야합니다.
다형적 참조
1
2
3
4
5
6
7
8
// Parent.java
package poly.Basic;
public class Parent {
public void parentMethod(){
System.out.println("parent.parentMethod");
}
}
1
2
3
4
5
6
7
8
// Child.java
package poly.Basic;
public class Child extends Parent{
public void childMethod(){
System.out.println("child.childMethod");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// PolyMain.java
package poly.Basic;
public class PolyMain {
public static void main(String[] args) {
// (1) 부모 변수가 부모 인스턴스 참조
System.out.println("Parent -> Parent");
Parent parent = new Parent();
parent.parentMethod();
// (2) 자식 변수가 자식 인스턴스 참조
Child child = new Child();
child.childMethod();
child.parentMethod();
// (3) 부모 변수가 자식 인스턴스 참조 (다형적 참조)
Parent poly = new Child();
poly.parentMethod();
// pChild.childMethod(); 자식 메서드 호출 불가능
}
}
다형적 참조를 이해하기 위한 예제코드입니다. Child 클래스는 Parent 클래스를 상속받았고 PolyMain에서 사용됩니다.
(1) 부모 변수가 부모 인스턴스 참조
Parent parent = new Parent();
이 경우 부모타입인 Parent를 생성했기 때문에, 메모리 상에 Parent만 생성됩니다 (자식은 생성되지 않습니다).
생성된 참조값을 Parent타입 변수인 parent에 담아두고 parent.ParentMethod()
를 호출하면 Parent
클래스에 있는 parentMethod()
가 호출됩니다.
(2) 자식 타입의 변수가 자식 인스턴스 참조
Child child = new Child();
이 경우 자식 타입인 Child를 생성했기 때문에 메모리상에 Child와 Parent가 모두 생성됩니다.
생성된 참조값을 Child 타입의 변수인 child에 담아둡니다.
child.childMethod()
를 호출하면 인스턴스의 Child
클래스에 있는 childMethod()
가 호출됩니다.
(3) 다형적 참조: 부모 타입의 변수가 자식 인스턴스 참조
Parent poly = new Child();
Child 인스턴스를 생성했기 때문에 메모리상에 Child와 Parent가 모두 생성됩니다.
생성된 참조값을 Parent 타입의 변수인 poly에 담아둡니다.
여기서 poly는 Parent(부모)타입이고, 생성된 인스턴스는 Child타입입니다.
자바에서 부모타입은 자식 타입을 담을 수 있습니다. Parent poly = new Child();
(가능)
반대로 자식 타입은 부모 타입을 담을 수 없습니다. Child child1 = new Parent();
(불가능)
다형성과 캐스팅
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package poly.Basic;
public class CastingMain {
public static void main(String[] args) {
Parent poly = new Child();
poly.parentMethod();
//poly.childMethod():
// 다운 캐스팅 가능
Child child = (Child) poly;
child.childMethod();
// 일시적 다운 캐스팅
((Child) poly).childMethod();
// 업캐스팅
Child child1 = new Child();
Parent parent1 = (Parent) child1; // 업캐스팅은 생략가능, 생략 권장
Parent parent2 = child1 // 업캐스팅 생략
}
}
Parent poly = new Child()
와 같이 부모 타입의 변수를 사용하게 되면 poly.childMethod()
와 같이 자식 타입에 있는 기능은 호출할 수 없습니다. 이럴 때에는 다운캐스팅을 사용할 수 있습니다.
Child child = (Child) poly;
일반 타입을 캐스팅 하는 것 처럼 코드를 작성하면 캐스팅이 됩니다. 다운캐스팅 된 변수는 childMethod를 호출할 수 있게됩니다.
반대로 Child 타입을 Parent 타입으로 캐스팅할 수도 있습니다. 이를 업캐스팅이라고 합니다.
다운캐스팅 주의점
업캐스팅과 다르게 다운캐스팅은 주의할 점이 있습니다. 바로 자식의 인스턴스가 생성되지 않았는데 캐스팅하는 경우로 코드로 보면 다음과 같습니다.
parent변수는 Parent 인스턴스를 생성했습니다. 즉, Child는 생성되지 않은 상태입니다. 이렇게 메모리에 존재하지 않는 Child로 캐스팅하려고 하면 심각한 런타입 오류가 발생합니다.
1
2
3
4
5
6
7
8
package poly.Basic;
public class CastingMain {
public static void main(String[] args) {
Parent parent = new Parent();
Child child = (Child) parent;
}
}
instanceof
다형성에서 참조형 변수는 다양한 자식을 대상으로 참조할 수 있습니다. 그런데 참조하는 대상이 다양하기 때문에 어떤 인스턴스를 참조하고 있는지 확인하고 싶다면 instanceof를 사용해 확인할 수 있습니다.
if (parent instanceof Child)
parent변수가 Child의 인스턴스라면 true, 아니라면 false를 반환해서 확인할 수 있습니다.
자바 16부터는 instanceof를 사용하면서 동시에 변수를 선언할 수 있습니다.
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
package poly.Basic;
public class CastingMain5 {
public static void main(String[] args) {
Parent parent1 = new Parent();
call(parent1);
Parent parent2 = new Child();
call(parent2);
}
private static void call(Parent parent){
// instanceof
if (parent instanceof Child){
System.out.println("Child 인스턴스 맞음");
Child child = (Child) parent;
child.childMethod();
}
else {
System.out.println("Child 인스턴스 아님");
}
// java16 부터 가능. instaceof 사용하면서 변수 선언
if(parent instanceof Child child){
child.childMethod();
}
}
}
다형성과 메서드 오버라이딩
다형성을 이루는 또 하나의 중요한 핵심 이론인 메서드 오버라이딩입니다. 메서드 오버라이딩에서 꼭 기억해야 할 점은 오버라이딩 된 메서드가 항상 우선권을 가진다는 점입니다.
1
2
3
4
5
6
7
8
9
10
// Parent.java
package poly.overriding;
public class Parent {
public String value = "parent";
public void method(){
System.out.println("Parent.method");
}
}
1
2
3
4
5
6
7
8
9
10
11
// Child.java
package poly.overriding;
public class Child extends Parent{
public String value = "child";
@Override
public void method() {
System.out.println("Child.metohd");
}
}
1
2
3
4
5
6
7
8
9
10
11
package poly.overriding;
public class OverridingMain {
public static void main(String[] args) {
// 부모 변수가 자식 인스턴스를 참조(다형적 참조)
Parent poly = new Child();
System.out.println("Parent -> Child");
System.out.println("value = " + poly.value); // 변수는 오버라이딩 x
poly.method(); // 메서드는 오버라이딩 o
}
}
poly는 Child 인스턴스를 업캐스팅한 Parent타입입니다.
poly.value
변수는 Parent타입에 있는 value값을 읽어옵니다. poly.method()
는 Parent타입에 있는 method를 실행하지않고, Child에 오버라이딩된 메서드가 항상 우선권을 갖기 때문에 Child.method()가 실행됩니다.