다형성
객체지향 프로그래밍의 대표적인 특징으로는 캡슐화, 상속, 다형성이 있다. 그 중에서 다형성은 객체지향 프로그래밍의 꽃이라 불린다.\
다형성(Polymorphism)은 이름 그대로 “다양한 형태”, “여러 형태”를 뜻한다.
프로그래밍에서 다형성은 한 객체가 여러 타입의 객체로 취급될 수 있는 능력을 뜻한다. 보통 하나의 객체는 하나의 타입으로 고정되어 있다. 그런데 다형성을 사용하면 하나의 객체가 다른 타입으로 사용될 수 있다는
뜻이다.
다형성을 이해하기 위해서는 크게 2가지 핵심 이론을 알아야 한다.
- 다형적 참조
- 메서드 오버라이딩
다형적 참조
- 부모 타입의 변수가 부모 인스턴스를 참조한다.
Parent parent = new Parent()
Parent
인스턴스를 만들었다. 이 경우 부모 타입인Parent
를 생성했기 때문에 메모리 상에Parent
만 생성된다.(자식은 생성되지 않는다)- 생성된 참조값을
Parent
타입의 변수인Parent
에 담아둔다 parent.parentMethod()
를 호출하면 인스턴스의Parent
클래스에 있는parentMethod()
가 호출된다.
- 자식 타입의 변수가 자식 인스턴스를 참조한다.
Child child = new Child()
Child
인스턴스를 만들었다. 이 경우 자식 타입인Child
를 생성했기 때문에 메머리 상에Child
와Parent
가 모두 생성된다.- 생성된 참조값을
Child
타입의 변수인child
에 담아둔다. child.childMethod()
를 호출하면 인스턴스의Child
클래스에 있는childMethod()
가 호출된다.
- 부모 타입의 변수가 자식 인스턴스를 참조한다.
Parent poly = new Child()
Child
인스턴스를 만들었다. 이 경우 자식 타입인Child
를 생성했기 때문에 메모리 상에Child
와Parent
가 모두 생성된다.- 생성된 참조값을
Parent
타입의 변수인poly
에 담아둔다.
부모는 자식을 담을 수 있다.
- 부모 타입은 자식 타입을 담을 수 있다.
Parent poly
는 부모 타입이다.new Child()
를 통해 생성된 결과는Child
타입이다. 자바에서 부모 타입은 자식 타입을 담을 수 있다!Parent poly = new Child()
: 성공
- 반대로 자식 타입은 부모 타입을 담을 수 없다.
Child child1 = new Parent()
: 컴파일 오류 발생
Parent poly = new Child()
이렇게 자식을 참조한 상황에서 poly
가 자식 타입인 Child
에 있는 childMethod()
를 호출하면 어떻게 될까?
- 컴파일 오류 발생
이런 경우 childMethod()
를 호출하고 싶으면 어떻게 해야할까? 바로 캐스팅이 필요하다.
다형성과 캐스팅
다운캐스팅
부모는 자식을 담을 수 있지만 자식은 부모를 담을 수 없다.
- Parent parent = new Child(): 부모는 자식을 담을 수 있다.
- Parent parent = child // Child child 변수 : 부모는 자식을 담을 수 있다.
캐스팅
- 업캐스팅(upcasting) : 부모 타입으로 변경
- 다운캐스팅(downcasting) : 자식 타입으로 변경
캐스팅 용어
“캐스팅”은 영어 단어 “cast”에서 유래되었다. “cast”는 금속이나 다른 물질을 녹여서 특정한 형태나 모양으로 만드는 과정을 의미한다.
일시적 다운 캐스팅
((Child)poly).childMethod() //다운캐스팅을 통해 부모타입을 자식 타입으로 변환 후 기능 호출
((Child)x001).childMethod() //참조값을 읽은 다음 자식 타입으로 다운캐스팅
업캐스팅
Parent parent1=(Parent)child;
- 업캐스팅은 생략할 수 있다.
- 다운캐스팅은 생략할 수 없다.
- 참고로 업캐스팅은 매우 자주 사용하기 때문에 생략을 권장한다.
다운캐스팅과 주의점
다운캐스팅이 가능한 경우
다운캐스팅이 불가능한 경우
new Parent()
로 부모 타입으로 객체를 생성한다.- 메모리 상에 자식 타입은 전혀 존재하지 않는다.
- 다운캐스팅을 하여도 메모리상에
Child
가 존재하지 않으므로 사용이 불가능하다. - 자바에서는 이런 경우
ClassCastException
예외를 발생시킨다.
업캐스팅이 안전하고 다운캐스팅이 위험한 이유
업캐스팅의 경우 이런 문제가 절대로 발생하지 않는다. 왜냐하면 객체를 생성하면 해당 타입의 상위 부모 타입은 모두 함께 생성된다.
반면에 다운캐스팅의 경우 인스턴스에 존재하지 않는 하위 타입으로 캐스팅하는 문제가 발생할 수 있다.
컴파일 오류 vs 런타임 오류
컴파일 오류는 변수명 오타, 잘못된 클래스 이름 사용등 자바 프로그램을 실행하기 전에 발생하는 오류이다. 이런 오류는 IDE에서 즉시 확인할 수 있기 때문에 안전하고 좋은 오류이다.
반면에 런타임 오류는 이름 그대로 프로그램이 실행되고 있는 시점에 발생하는 오류이다. 런타임 오류는 매우 안좋은 오류이다. 왜냐하면 보통 고객이 해당 프로그램을 실행하는 도중에 발생하기 때문이다.
instanceof
다형성에서 참조형 변수는 이름 그대로 다양한 자식을 대상으로 참조할 수 있다. 그런데 참조하는 대상이 다양하기 때문에 어떤 인스턴스를 참조하고 있는지 확인하려면 어떻게 해야할까?
다형성과 메서드 오버라이딩
다형성을 이루는 또 하나의 중요한 핵심 이론은 바로 메서드 오버라이딩이다.
메서드 오버라이딩에서 꼭! 기억해야 할 점은 오버라이딩 된 메서드가 항상 우선권을 가진다는 점이다.
그래서 이름도 기존 기능을 덮어 새로운 기능을 재정의 한다는 뜻의 오버라이딩이다.
메서드 오버라이딩의 진짜 힘은 다형성과 함께 사용할 때 나타난다.
Parent
,Child
모두value
라는 같은 멤버 변수를 가지고 있다.- 멤버 변수는 오버라이딩 되지 않는다.
Parent
,Child
모두method()
라는 같은 메서드를 가지고 있다.Child
에서 메서드를 오버라이딩 했다.- 메서드는 오버라이딩 된다.
child
변수는Child
타입이다. 따라서child.value
,child.method()
를 호출하면 인스턴스의Child
타입에서 기능을 찾아서 실행한다.
parent
변수는Parent
타입이다. 따라서parent.value
,parent.method()
를 호출하면 인스턴스의Parent
타입에서 기능을 찾아서 실행한다.
- 이 부분이 중요하다.
poly
변수는Parent
타입이다. 따라서poly.value
,poly.method()
를 호출하면 인스턴스의Parent
타입에서 기능을 찾아서 실행한다.poly.value
:Parent
타입에 있는value
값을 읽는다.poly.method()
:Parent
타입에 있는method()
를 실행하려고 한다. 그런데 하위 타입인Child.method()
가 오버라이딩 되어 있다. 오버라이딩 된 메서드는 항상 우선권을 가진다. 따라서Parent.method()
가 아니라Child.method()
가 실행된다.
오버라이딩 된 메서드는 항상 우선권을 가진다. 오버라이딩은 부모 타입에서 정의한 기능을 자식 타입에서 재정의하는 것이다. 만약 자식에서도 오버라이딩 하고 손자에서도 같은 메서드를 오버라이딩을 하면 손자의 오버라이딩 메서드가 우선권을 가진다. 더 하위 자식의 오버라이딩 된 메서드가 우선권을 가지는 것이다.
- 다형적 참조 : 하나의 변수 타입으로 다양한 자식 인스턴스를 참조할 수 있는 기능
- 메서드 오버라이딩 : 기존 기능을 하위 타입에서 새로운 기능으로 재정의