[Java] OOP - 다형성, 참조변수의 형변환, instanceof 연산자

선영·2022년 9월 21일
0
post-thumbnail

다형성(polymorphism)

다형성이란, '여러 가지 형태를 가질 수 있는 능력' 을 의미함. 한 타입의 참조변수로 여러 타입의 객체를 참조할 수 있도록 함으로써 다형성을 프로그램적으로 구현함. 조상클래스 타입의 참조변수로 자손클래스의 인스턴스를 참조할 수 있음.

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 t = new Tv();
SmartTv s = new SmartTv();

위와 같이 인스턴스를 다루기 위해서, 인스턴스의 타입과 일치하는 타입의 참조변수를 사용함. 그러나 Tv와 SmartTv클래스와 같이 서로 상속관계에 있는 경우, 조상 클래스 타입의 참조변수로 자손 클래스의 인스턴스를 참조하는 것이 가능함.

Tv t = new SmartTv(); // 타입 불일치. 조상 타입의 참조변수로 자손 인스턴스 참조

🙄 인스턴스를 같은 타입의 참조변수로 참조하는 것과 조상타입의 참조변수로 참조하는 것의 차이는 무엇일까?

SmartTv s = new SmartTv(); // 참조변수와 인스턴스 타입 일치
Tv t = new SmartTv(); // 조상 타입 참조변수로 자손 타입 인스턴스 참조
SmartTv s = new Tv(); // 에러!! 허용 안됨.

👉 둘 다 같은 타입의 인스턴스지만, 참조변수의 타입에 따라 사용할 수 있는 멤버의 개수가 달라짐.

  • 조상타입의 참조변수로 자손타입의 인스턴스 참조할 수 있음.
  • 반대로 자손타입의 참조변수로 조상타입의 인스턴스는 참조할 수 없음.

참조변수의 형변환

기본형 변수처럼 참조변수도 형변환이 가능함. 단, 서로 상속관계에 있는 클래스 사이에서만 가능하기 때문에 자손타입의 참조변수를 조상타입의 참조변수로, 조상타입의 참조변수를 자손타입의 참조변수로의 형변환만 가능함. 바로 윗 조상이나 자손이 아닌, 조상의 조상으로도 형변환이 가능함. 따라서, 모든 참조변수는 모든 클래스의 조상인 Object 클래스 타입으로 형변환이 가능함.

class Car {	}
class FireEngine extends Car { }
class Ambulance extends Car { } 

FireEngine f = new FireEngine();
Car c = (Car)f; // OK 조상인 Car타입으로 형변환(생략가능)
FireEngine f2 = (FireEngine)c; // OK 자손인 FireEngine타입으로 형변환(생략불가)
Ambulance a = (Ambulance)f; // 에러!! 상속관계가 아닌 클래스 간의 형변환 불가!

기본형의 형변환과 달리 참조형의 형변환은 변수에 저장된 값(주소값)이 변환되는 것이 아니라, 그저 참조변수를 다른 종류로 바꾸는 것임. 참조변수의 타입을 바꾸는 이유는 사용할 수 있는 멤버 개수를 조절하기 위한 것임.
조상타입으로 형변환을 생략할 수 있는 이유조상타입으로 형변환을 하면 다룰 수 있는 멤버의 개수가 줄어들기 때문에 항상 안전하기 때문임. 반대로 실제 객체가 가진 기능보다 참조변수의 기능이 더 많으면 안됨.


instanceof 연산자

참조변수가 참조하고 있는 인스턴스의 실제 타입 을 알아보기 위해 instanceof 연산자 를 사용함. 주로 조건문 에 사용되며, instanceof 의 왼쪽 에는 참조변수 를, 오른쪽 에는 타입(클래스명) 이 피연산자로 위치함. 그리고 연산의 결과boolean 값인 true와 false 중의 하나를 반환함.
instanceof를 이용한 연산결과로 true를 얻었다는 것은 참조변수가 검사한 타입으로 형변환이 가능하다는 것을 의미함.
값이 null 인 참조변수에 대해 instanceof 연산 을 수행하면 false 를 결과로 얻음.

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; 
}    

위의 코드에서 Buyer 클래스 에 물건을 구입하는 기능의 메서드를 추가함.

	void buy(Tv t) {
    	// Buyer가 가진 돈(money)에서 제품의 가격(t.price)만큼 뺀다.
        money = money - t.price; 
        // Buyer의 보너스점수에 제품의 보너스점수를 더한다. 
        bonusPont = bonusPoint + t.bonusPoint; 
    }    

buy(Tv t) 로는 Tv밖에 살 수 없기 때문에 아래와 같이 다른 제품들도 구입할 수 있는 메서드를 추가함.

	void buy(Computer c) {
    	money = money - c.price; 
        bonusPoint = bonusPoint + c.bonusPoint; 
    }    

이렇게 되면, 제품의 종류가 늘어날 때마다 Buyer 클래스 에는 새로운 buy 메서드 를 추가해주어야 함. 그러나 메서드의 매개변수에 다형성을 적용하면 하나의 메서드로 간단히 처리할 수 있음.

	void buy(Product p) {
    	money -= p.price; 
        bonusPoint += p.bonusPoint; 
    }    

위의 코드처럼 Product 타입의 참조변수 로 처리하면, 메서드의 매개변수로 Product 클래스의 자손타입의 참조변수 면 어느 것이나 매개변수로 받아들일 수 있음. 다른 제품을 추가할 때 Product 클래스 를 상속받기만 하면 됨.

	Buyer b = new Buyer(); 
    Tv t = new Tv(); 
    Computer c = new Computer(); 
    b.buy(t); 
    b.buy(c); 
    
    // Tv t = new Tv(); 
    // b.buy(t);
    // 한 문장으로 줄이면 b.buy(new Tv()); 

0개의 댓글

관련 채용 정보