●다형성
다형성이란 '여러 가지 형태를 가질 수 있는 능력'이다. 자바에서는 한 타입의 참조변수로 여러 타입의 객체를 참조할 수 있도록 한다. 이를 다형성이라고 한다. 구체적으로 말하자면 조상클래스 타입의 참조변수로 자손클래스의 인스턴스를 참조할 수 있도록 하는 것이다.
class Tv {
boolean power;
int channel;
void power() { power = !power }
void channelUp() { ++channel; }
void channelDown() { --channel; }
}
class SmartTv extends Tv {
String text;
void caption() { /* 내용생략 */ }
}
지금까지 우리는 생성된 인스턴스를 다루기 위해서, 인스턴스의 타입과 일치하는 타입의 참조변수만을 사용했다. 즉, Tv인스턴스를 다루기 위해서는 Tv타입의 참조변수를 사용하고, SmartTv인스턴스를 다루기 위해서는 SmartTv타입의 참조변수를 사용했다.
Tv t = new Tv();
SmartTv s = new SmartTv();
위의 코드처럼 인스턴스의 타입과 참조변수의 타입이 일치하는 것이 보통이지만, Tv와 SmartTv클래스가 서로 상속관계에 있을 경우, 다음과 같이 조상 클래스 타입의 참조변수로 자손 클래스의 인스턴스를 참조하도록 하는 것도 가능하다.
Tv t = new SmartTv(); //타입 불일치. 조상 타입의 참조변수로 자손 인스턴스를 참조
인스턴스를 같은 타입의 참조변수로 참조하는 것과 조상타입의 참조변수로 참조하는 것은 어떤 차이가 있는지 확인해보자.
<참조 변수와 인스턴스의 타입이 일치하는 경우>
SmartTv s = new SmartTv(); // 참조 변수와 인스턴스의 타입이 일치
개념을 설명하기 위해 참조변수와 인스턴스를 그림을 참조하는데 보는 바와 같이 참조 변수와 인스턴스의 타입이 일치할 경우 참조 변수 s는 SmartTv인스턴스의 모든 멤버를 사용할 수 있다.
<조상 타입 참조변수로 자손 타입 인스턴스 참조>
Tv t = new SmarTv(); //조상 타입 참조변수로 자손 타입 인스턴스 참조
다음은 조상 타입 참조변수로 자손 타입 인스턴스를 참조한 경우를 그림으로 표현한 것이다. 그림에서 보는 바와 같이 조상 타입 참조변수 t는 자손 타입 인스턴스 보다 적은 멤버변수를 갖기 때문에 text와 caption()멤버 변수를 가질 수 없다. 두 예시를 통해 참조변수의 타입과 인스턴스 변수의 타입 관계에 따라 사용할 수 있는 멤버변수의 갯수가 달라는 걸 알 수 있다. 그렇다면 만약 자손 타입의 참조변수가 조상 타입의 인스턴스를 참조하면 어떻게 될까? 즉 멤버변수를 더 많이 가지고 있는 타입에서 멤버변수를 적게 갖고 있는 타입을 참조하면 어떻게 될까? 원래갖고 있던 멤버변수를 사용하지 못하는 불상사가 발생하게 된다. 그래서 자손 타입의 참조변수로는 조상타입의 인스턴스를 참조할 수 없다.
SmartTv s = new Tv(); //에러. 허용 안 됨.
여기서 우리는 다형성의 두 가지 성질을 이해할 수 있다.
조상타입의 참조변수로 자손타입의 인스턴스를 참조할 수 있다.
반대로 자손타입의 참조변수로 조상타입의 인스턴스를 참조할 수는 없다.
●참조변수의 형변환
참조변수도 기본형 변수와 마찬가지로 형변환이 가능하다. 단, 서로 상속관계에 있는 클래스 사이에서만 가능하기 때문에 자손 타입의 참조변수를 조상타입의 참조변수로, 조상타입의 참조변수를 자손타입의 참조변수로의 형변환만 가능하다. 같은 조상을 상속했어도 자손, 조상의 관계가 아니면 형변환이 불가능하다.
class Car {}
class FireEngine extends Car{}
class Ambulance extends Car{}
클래스 Car와 이를 상속받는 FireEngine클래스와 Ambulance클래스가 있다고 가정해보자. FireEngine클래스와 Ambulance클래스는 Car클래스의 자손이기 때문에 서로 형변환이 가능하다. 하지만 조상, 자손 관계에 있지않은 두 클래스는 형변환이 불가능하다.
FireEngine f = new FireEngine();
Car c = (Car)f; //OK, 조상인 Car 타입으로 형변환(생랙가능)
FireEngine f2 = (FireEngine)c; //OK, 자손인 FireEngine타입으로 형변환(생략불가)
Ambulance a = (Ambulance)f; //에러. 상속관계가 아닌 클래스 간의 형변환 불가
Car c = (Car)f; // f의 값(객체의 주소)을 c에 저장.
// 타입을 일치시키기 위해 형변환 필요(생략가능)
f = (FireEngine) c; //조상타입을 자손타입으로 형변환하는 경우 생략불가
기본형의 형변환과 달리 참조형의 형변환은 변수에 저장된 값(주소값)이 변환되는 것이 아니다. 그저 참조변수를 다른 종류의 것으로 바꾸는 것 뿐이다. 또한 조상 타입으로의 형변환을 생략할 수 있는 이유는 조상타입으로 형변환하면 다룰 수 있는 멤버의 개수가 줄어들기 때문에 항상 안전하기 때문이다.
●instanceof 연산자
참조변수가 참조하고 있는 인스턴스의 실제 타입을 알아보기 위해 instanceof 연산자를 사용한다. 주로 조건문에 사용되며, instanceof의 왼쪽에는 참조변수를 오른쪽에는 타입(클래스명)이 피연산자로 위치한다. 그리고 연산의 결과로 boolean값인 true와 false 중의 하나를 반환한다.
instanceof를 이용한 연산결과로 true를 얻었다는 것은 참조변수가 검사한 타입으로 형변환이 가능하다는 것을 뜻한다.
void doWork(Car c) {
if (c instanceof FireEngine) { // 1. 형변환이 가능한지 확인
FireEngine fe = (FireEngine)c; // 2.형변환
fe.water();
...
●매개변수의 다형성
참조변수의 다형적인 특징은 메서드의 매개변수에도 적용된다.
class Product {
int price; // 제품의 가격
int bonusPoint; // 제품구매시 제공하는 보너스점수
}
class Tv extends Product {}
class Computer extends Product {}
class Buyer { // 고객, 물건을 사는 사람
int money = 1000; // 소유금액
int bonusPoint = 0; // 보너스점수
void buy(tv t) { //Buyer클래스가 물건을 구입하는 기능의 메서드
money = money = t.price; // Buyer가 가진 돈(money)에서 제품의 가격(t.price)만큼 뺀다.
bonusPoint = bonusPoint + t.bonusPoint; // Buyer의 보너스점수(bonusPoint)에 제품의 보너스점수(t.bonusPoint)를 더한다.
}
위의 코드에서 buy(Tv t)는 제품을 구입하면 제품을 구입한 사람이 가진 돈에서 제품의 가격을 빼고, 보너스 점수는 추가하는 작업을 하도록 작성되었다. 그런데 Tv말고 Computer가 구매하고 싶으면 위의 코드에서 Computer를 구매하는 메서드를 추가해야한다.
void buy(Computer c) {
money = money - c.price;
bonusPoint = bonusPoint + c.bonusPoint;
}
만일 다른 제품들을 구매하면 메서드를 추가했던 것과 마찬가지로 제품을 구매할 때 마다 메서드를 추가하는 번거로움이 발생할거다. 이런 번거러움을 없애기 위해 메서드의 매개변수에 다형성을 적용한다.
void buy(Product p) {
money -= p.price;
bonusPoint += p.bonusPoint;
}
매개변수가 Product타입의 참조변수라는 것은, 메서드의 매개변수로 Product클래스의 자손타입의 참조변수면 어느 것이나 매개변수로 받아들일 수 있다는 뜻이다. 그리고 Product 클래스에 price와 bonusPoint가 선언되어 있기 때문에 참조변수 p로 인스턴스의 price와 bonusPoint를 사용할 수 있기에 이와같이 할 수 있다. 앞으로 다른 제품 클래스를 추가할 때 Product클래스를 상속받기만 하면, buy(Product p)메서드의 매개변수로 받아들여질 수 있다. 매개변수의 다형성을 통해 앞서 번거로웠던 문제가 해결됐다.
출처:자바의 정석 기초편(저자: 남궁성, 출판사: 도우출판)
'java > 객체지향' 카테고리의 다른 글
[객체지향] 추상 클래스 (0) | 2021.07.16 |
---|---|
[객체지향]여러 종류의 객체를 배열로 다루기 (0) | 2021.07.16 |
[객체지향] 제어자 (0) | 2021.07.14 |
[객체지향] super와 super() (0) | 2021.07.14 |
[객체지향]오버라이딩(overriding) (0) | 2021.07.13 |