객체지향 프로그래밍(2) - 다형성
다형성 개념은 클래스와 인터페이스를 활용함에 있어서 가장 중요한 핵심 개념이므로 자세히 알아두어야할 필요가 있다.
다형성(polymorphism) 이란?
여러 가지 형태를 가질 수 있는 능력을 말한다.
Java에서는 조상 타입의 참조 변수로 자손타입의 객체를 다루는 것을 말한다.
Tv ctv = new CaptionTv(); // 부모 클래스의 타입 = 인스턴스 타입
부모 타입의 참조변수로 자손타입의 객체를 담아 사용하면 어떤 차이가 발생할까?
사용할 수 있는 멤버의 갯수가 제한되게 된다.
상속의 개념을 토대로 설명할 수 있다.
자손 클래스는 부모 클래스를 담아 이전보다 확장된 범위를 가지게 된다.
부모의 멤버 <= 자손의 멤버
따라서, 자손타입에는 명시된 멤버가 부모 타입에는 없을 수도 있다.
부모타입의 참조변수로 자손타입의 객체를 사용할 경우, 자손타입의 멤버는 사용할 수 없다.
참조변수의 형변환
서로 상속관계에 있는 클래스 간에, 참조변수의 형변환이 가능하다.
기본형 변수의 형변환에서 범위가 작은 자료형에서 큰 자료형의로의 형변환의 생략이 가능하듯이 참조 변수의 형변환에서도 형변환을 생략할 수 있다.
Up-casting : 자손 타입 ➞ 부모 타입
Tv t = new CaptionTv(); // 형변환 생략 가능
Down-casting : 부모 타입 ➞ 자손 타입 (형변환 생략 불가)
Tv t = new CaptionTv();
CaptionTv Ctv = (CaptionTv)t;
참조변수에 연결된 객체가 자손타입이라는 가정 하에 부모 타입의 참조변수를 다시 자손타입으로 형변환 할 수 있다.
형변환 후에는, 다시 자손의 멤버를 전부 사용할 수 있게 된다.
instanceof 연산자
참조변수의 형변환 시에, 반드시 instanceof 연산자를 사용하여 형변환 가능 여부를 체크한 후에 형변환을 진행해야한다.
if(t intanceof CaptionTv){ // 형변환이 가능할 경우 true 반환
CaptionTv ctv = (CaptionTv)t;
}
그렇다면, 애초에 인스턴스의 모든 멤버를 사용할 수 있도록 자손타입의 참조변수로 객체를 연결해주면 될텐데 굳이 부모타입의 참조변수로 받아야만 하는 것일까? 하는 의문점이 들 것이다.
아래 매개변수의 다형성 예시를 통해 다형성의 장점을 어느정도 짐작해볼 수 있을 것이다.
매개변수의 다형성
class Product{
int price;
}
class Tv extends Product{}
class Computer extends Product{}
class Audio extends Product{}
// 각 클래스별 price 변수 초기화 부분 생략
class Buyer{
int money = 1000;
}
만약 제품을 구매할때마다 구매자의 돈을 차감하는 프로그램을 만든다고 가정해보자.
Tv를 구매한다고 하였을 때, Buyer 클래스에 메서드를 정의해보면 다음과 같을 것이다.
void buy(Tv t){
money = money - t.price;
}
하지만 이 메서드는 Tv 만을 다루지 못하므로 다른 물건을 구입할 때 마다 메서드를 오버로딩해서 한 개씩 추가해줘야만 할 것이다. 그런 번거로움을 아래 코드로 해결할 수 있다.
void buy(Product p){
money = money - p.price;
}
class Main{
public static void main(String args[]){
Buyer b = new Buyer();
Tv t = new Tv();
Computer c = new Computer();
b.buy(t);
c.buy(c);
}
}
부모타입의 참조변수로 모든 자손타입의 객체를 담을 수 있으므로, 한개의 메서드 만으로 모든 자손타입의 객체를 처리할 수 있다.