본문 바로가기
개발/JAVA

다형성

by 욤냠냠냠 2023. 4. 18.

📌 다형성이란?

객체지향개념에서 다형성이란 "조상클래스 타입의 참조변수로 자손클래스의 인스턴스를 참조할 수 있도록 한 것" 이다.

 

class Tv{
	boolean power;	//전원 상태
    int channel; 	//채널
    
    void power(){ power = !power; }
    void channelUp(){ ++channel; }
    void channelDown(){ --channel; }
    
 }
 
 class CaptionTv extends Tv {
 	String text;  // 캡션을 보여주기 위한 문자열
    void caption() { 내용 생략 }
    
 }
 
// 보통 생성된 인스턴스를 다루기 위해서 인스턴스 타입과 일치하는 타입의 참조변수를 사용했다. 
 Tv t = new Tv();
 CaptionTv c = new CaptionTv();

 

 

 

만약 참조변수와 다른 타입의 인스턴스를 참조한다면 ?

CaptionTv c = new CaptionTv();
Tv t = new CaptionTv();

 

위 코드는 생성된 인스턴스를 각각 c,t에 참조 하도록 하였다. 이 경우 실제 인스턴스가  CaptionTv 타입일지라도, t는 CaptionTv의 모든 멤버변수를 사용할 수 없고, Tv클래스 멤버 변수(상속받은 멤버 포함) 만 사용할 수 있다. 

생성된 CaptionTv 멤버변수 중에서  t.text와 t.caption은 사용할 수 없다는 뜻이다.

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

 

반대로 자손타입의 참조 변수로 조상클래스의 인스턴스를 참조 한다면?

CaptionTv c = new Tv();

위 코드를 컴파일한다면 에러가 일어난다. 왜냐하면 실제 인스턴스인 tv의 멤버 변수보다 참조변수인 c가 사용할 수 있는 멤버 개수가 더 많기 때문이다. 참조변수 c는 Tv인스턴스를 참조하고 있기때문에  Tv인스턴스에는 없는 text() 와 caption()을 사용하려고 한다면 문제가 발생한다.

 

자손타입의 참조변수로 조상타입의 인스턴스를 참조하는 것은 존재하지 않는 멤버를 사용하고자 할 가능성이 있기때문에
허용하지 않는다. 참조변수가 사용할 수 있는 멤버변수는 참조하는 인스턴스 멤버 변수보다 적거나 같아야 한다.

 

조상타입의 참조변수로 자손클래스의 인스턴스를 참조 할 수 있다.
반대로 자손타입의 참조변수로 조상클래스의 인스턴스는 참조 할 수 없다.
(참조변수가 사용할 수 있는 멤버개수가 인스턴스보다 많기 때문.)

 

📌 참조변수와 형변환

기본형 변수와 같이 참조변수도 형변환이 가능하다. 단, 상속관계에 있는 클래스 사이에서만 가능하다.

자손타입의 참조변수를 조상타입의 참조변수로, 조상타입의 참조변수를 자손타입의 참조변수로의 형변환만 가능하다.

 

자손타입 --> 조상타입(Up-casting) : 형변환 생략가능
자손타입 <-- 조상타입(Down-casting) : 형변환 생략 불가

헷갈려서 나는 시간에 비유하였다. 아이(자손)이 어른(조상)이 되려면 가만히만 있어도 되지만,
어른(조상)이  아이(자손)가 되려면 시간을 되돌려야 한다.
Class CastingTest1 {
	public static void main(String[] args){
    	   Car car = null;
   		   FireEngine fe = new FireEngine();
 		   FireEngine fe2 = null;
   		   
		fe.water();
		car = fe;			 //업캐스팅(생략가능) : 자손이 조상으로 형변환시 
//		car.water(); 		//컴파일 에러 1: Car타입의 참조변수로는 water()를 호출할 수 없다
//		fe = (FireEngine)car;//컴파일 에러 2: 조상타입의 인스턴스를 자손타입의 참조변수로 참조하는건 허용X.
		fe2 = (FireEngine)car;//다운캐스팅(생략불가능) : 조상이 자손으로 형변환시
		fe2.water();         
		

class Car {
	String color;
    int door;
    void drive() {
    	System.out.println("drive, Brrrr~" ); //운전하는 기능
    }
    void stop() {
    	System.out.println("Stop!!" ); //멈추는 기능
    }    
}

class FireEngine extends Car { //소방차
	void water(){				//물 뿌리는 기능
    	System.out.println("Water!!" );
        }
   }

class Ambulance extends Car { //구급차
	void siren(){				//싸이렌
    	System.out.println("Siren!!" );
        }
   }

------------------------------------------------------------------------
실행 결과 : Water!!
	  Water!!

 

형변환은 참조변수의 타입을 변환하는 것이지 인스턴스를 변환하는 것이 아니다. 그래서

참조변수의 형변환은 인스턴스에 아무런 영향을 미치지 않는다. 단지 참조변수의 형변환을 통해서

참조하고 있는 인스턴스에서 사용할 수 있는 멤버의 범위(개수)를 조절할 뿐이다.

 

서로 상속관계에 있는 타입간의 형변환은 양방향으로 자유롭지만,

참조변수가 가르키는 인스턴스의 자식타입으로는 형변환은 허용되지않는다.

 

참조변수가 가리키는 인스턴스의 타입이 무엇인지 확인하는것이 가장 중요하다.

 

🎈 참고

자바의 정석

'개발 > JAVA' 카테고리의 다른 글

오버로딩 과 오버라이딩  (0) 2023.04.11
ORM  (0) 2023.03.08
JVM(Java Virtual Machine)?  (4) 2023.03.08