자바의 정석 책에선 다형성 개념 역시 객체 지향 언어에서 주된 특징이라고 설명하고 있다.
비록 해당 개념은 타입이 코딩에 크게 영향을 끼치지 않는 자바스크립트에선 거의 경험하진 못했다.
하지만 정적 타입 언어인 자바에선 핵심적인 개념이다.
객체 지향 언어에서 다형성은 여러 형태를 가질 수 있는 능력이고, 자바는 한 타입의 참조 변수로 여러 타입의 객체를 참조하는 방식으로 다형성을 구현한다.
참조 변수로 다른 타입의 객체를 참조한다는 건, 변수 앞에 붙는 타입과 new 키워드 뒤에 사용되는 생성자 타입이 다를 수 있다는 것이다.
자바의 다형성은 상속 개념과 매우 밀접하게 연결되는데, 부모 클래스 타입의 참조 변수로 자식 클래스의 인스턴스를 참조할 수 있기 때문이다.
class ObjectOrientedLanguage {
String name;
int studentNumber;
public String getName () { return this.name; }
void addStudent () { ++studentNumber; }
}
class JavaScript extends ObjectOrientedLanguage {
boolean vanilla;
void reAct {...}
}
class Java extends ObjectOrientedLanguage {
float version;
void springBoot {...}
}
위에 두 클래스의 멤버 범위를 비교하자면, 자식 클래스인 자바스크립트가 OOL보다 넓다.
이때, ObjectOrientedLanguage js = new JavaScript()
이렇게 부모 타입의 참조 변수로 자식 클래스의 인스턴스를 참조할 수 있다. 이와 같은 방식으로 생성된 js는 자바스크립트 클래스의 모든 멤버를 참조할 수 없고, 자바스크립트의 부모 클래스인 OOP의 멤버만 사용할 수 있다.
물론 그 반대의 경우는 불가하다. 즉, 참조변수가 사용할 수 있는 멤버는 오직 인스턴스 멤버 개수 이하이어야 한다.
그리고 자식 타입과 부모 타입 서로 간에 형변환은 가능하다. 하지만 자식에서 부모로 바뀌는 Up-casting
방식에선 그렇지 않지만, 부모에서 자식으로 바뀌는 Down-casting
방식에선 형변환했음을 필수적으로 기입하는 명시적 형변환
을 해야 한다.
그 이유로, 앞서 범위가 더 넓은 자식 클래스의 멤버가 더 많다고 설명했다. 그렇기 때문에 실제 인스턴스의 멤버 개수보다 참조 변수의 멤버 개수가 많아 생길 수 있는 문제를 사전에 방지해야 하기 때문이다.
부모 타입을 상속하는 두 자식 클래스는 별개의 클래스이기 때문에, 서로 형변환은 불가하다.
ObjectOrientedLanguage c;
Java jsp;
JavaScirpt node;
c = (ObjectOrientedLanguage)jsp;
// 가능. 다만, 부모 참조 변수가 자식 타입으로 형변환할 때 생략 가능하다.
node = c;
// Compile Error : 자식 참조 변수가 부모 타입으로 형변환할 때 타입 생략 불가
jsp = (Java)node;
// Compile Error : 상속 관계가 아닌 클래스 간 형변환 불가.
Java spring = new ObjectOrientedLanguage();
// Compile Error : 자식 타입의 참조 변수는 부모의 인스턴스를 참조할 수 없다.
형변환은 참조변수의 타입을 바꾸는 거지 인스턴스 자체를 변환하는 게 아니기에 인스턴스엔 아무런 영향도 주지 않는다. 다만 참조변수가 해당 인스턴스에서 사용할 수 있는 멤버 수를 조절해주는 것.
그러나, 여기서 자식 클래스가 부모 클래스의 메서드를 오버라이딩한다면, 형변환된 참조 변수는 자식 클래스의 오버라이딩된 메서드를 우선적으로 참조한다.
ObjectOrientedLanguage oop = new Java(); // 를
⬇️
Java c = new Java();
ObjectOrientedLanguage oop = (ObjectOrientedLanguage) c;
// 위 두 줄로 풀어 쓴 거라고 우선은 생각하자.
oop instanceof Java == true
가 나오면 검사한 타입으로 형변환이 가능하다는 뜻이니깐.
oop.getClass().getName() == ObjectOrientedLanguage
처럼 참조변수가 가리키는 인스턴스의 클래스 이름을 문자열로 반환받을 수도 있다.
멤버 변수가 부모와 자식 클래스에 중복으로 정의됐을 때, 부모 타입의 참조변수를 사용하면 부모 클래스에 선언된 멤버 변수가 사용되고, 자식 타입일 경우 자식 클래스의 멤버가 사용된다.
매개 변수에도 역시 다형성을 적용해 부모 타입의 매개변수만으로 자식 타입의 여러 인스턴스에서도 해당 메소드를 활용할 수 있다.
비슷한 맥락으로, 부모 타입의 참조 변수 배열 선언문 하나만으로 자식 클래스의 여러 배열을 생성할 수 있다. 이때 Vector
클래스를 활용해 배열을 동적으로 관리할 수 있다.
유데미 바로가기 / STARTERS 취업 부트캠프 공식 블로그 보러가기
[도우출판]Java의 정석 3rd Edition, - 남궁성