Java/자바의 정석 3판

객체지향 프로그래밍(2) - 다형성

나도관 2023. 1. 25. 14:50

다형성 개념은 클래스와 인터페이스를 활용함에 있어서 가장 중요한 핵심 개념이므로 자세히 알아두어야할 필요가 있다.

 

다형성(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);
    }
}

 

부모타입의 참조변수로 모든 자손타입의 객체를 담을 수 있으므로, 한개의 메서드 만으로 모든 자손타입의 객체를 처리할 수 있다.