* JAVA - 다형성(polymorphism) (1)

jodbsgh·2022년 3월 23일
0

💡"JAVA"

목록 보기
23/67

다형성이란?

객체지향개념에서 다형성이란 '여러 가지 형태를 가질 수 있는 능력'이다.

조상클래스 타입의 참조변수로 자손클래스의 인스터스를 참조할 수 있도록 하였다.

class Tv{
	boolean power;	//전원상태 (on / off )
    int channel;	//채널
    
    void power()	{ power =! power;	}
    void channelUp()   {  ++channel;	}
    void channelDown() { -- channel;	}
}
class CaptionTv extends TV {
	String text;	  // 캡션을 보여 주기 위한 문자열
    void caption()	{ //내용생략 }
}

클래스 Tv와 CaptionTv는 서로 상속관계에 있으며, 이 두 클래스의 인스턴스를 생성하고 사용하기 위해서는 다음과 같이 할 수 있다.

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

이처럼 인스턴스의 타입과 참조변수의 타입이 일치하는 것이 보통이지만, Tv와 CaptionTv클래스가 서로 상속관계에 있을 경우, 다음과 같이 조상 클래스 타입의 참조변수로 자손 클래스의 인스턴스를 참조하도록 하는 것도 가능하다.

	CaptionTv c = new CaptionTv();
    Tv 		  t = new CaptionTv();
  • 이 경우 실제 인스턴스가 CaptionTv타입이라고 할지라도, 참조변수 t로는 CaptionTv 인스턴스의 모든 멤버를 사용할 수 없다.
  • Tv타입의 참조변수로는 CaptionTv인스턴스 중에서 Tv클래스의 멤버들만 사용할 수 있다.
  • 둘 다 같은 타입의 인스턴스지만 참조변수의 타입에 따라 사용할 수 있는 멤버의 개수가 달라진다.

반대로 자손타입의 참조변수로 조상타입의 인스턴스를 참조하는 것은 불가능하다. 참조변수가 사용할 수 있는 멤버의 개수는 인스턴스의 멤버 개수보다 같거나 적어야 한다.

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

참조변수의 형변환

서로 상속관계에 있는 클래스사이에서만 가능하기 때문에 자손타입의 참조변수를 조상타입의 참조변수로, 조상타입의 참조변수를 자손타입의 참조변수로의 형변환만 가능하다.

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

class Car{
	String color;
    int door;
    void drive() {		//운전하는 기능
    	System.out.println("drive, Brrr~");
    }
    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~~~");
    }
}

자바에서는 조상과 자식관계만 존재하기 때문에 FireEngine과 Ambulance클래스는 서로 아무런 관계가 없다.

따라서 Car타입의 참조벼수와 FireEngine타입의 참조변수 그리고 Car타입의 참조변수와 Ambulance타입의 참조변수 간에는 서로 형변환이 가능하지 않다.


	FireEngine f;
    Ambulance a;
	a = (Ambulance)f;		//에러! 상속관계가 아닌 클래스간의 형변환 불가
    f = FireEngine)a;		//에러! 상속관계가 아닌 클래스간의 형변환 불가
Car타입 참조변수와 FireEngine타입 참조변수 간의 형변환 예)


Car car = null;
FireEngine fe = new FireEngine();
FireEngine fe2 = null;

car = fe;
fe2 = (FireEngine)car
  • 참조변수 car와 fe 타입이 서로 다르기 때문에, 대입연산을 수행하기 전에 형변환을 수행하여 두 변수간의 타입을 맞춰줘야함
  • 자손타입의 참조변수를 조상타입의 참조변수에 할당할 경우 형변환을 생략할 수 있다.
  • 조상타입의 참조변수를 자손타입의 참조변수에 저장할 경우 형변환을 생략할 수 없다.

Car타입의 참조변수 c를 Car타입의 조상인 Object타입의 참조변수로 형변환 하는 것은 참조변수가 다룰 수 있는 멤버의 개수가 실제 인스턴스가 갖고 있는 멤버의 개수보다 적을 것이 분명하므로 문제가 되지 않는다. 그래서 형 변환을 생략할 수 있도록 한 것이다.

형변환은 참조변수의 타입을 변환하는 것이지 인스턴스를 변환하는 것은 아니기 때문에 참조변수의 형변환은 인스턴스에 아무런 영향을 미치지 않는다.

단지 참조변수의 형변환을 통해서, 참조하고 있는 인스턴스에서 사용할 수 있는 멤버의 범위(개수)를 조절하는 것 뿐이다.

	Tv tv = new Caption();
    
    
    //위와 동일한 코드
    // 둘로 분해해서 작성한 것.
    CaptionTv c = new CaptionTv();
    Tv t = (Tv)c;
class CastingTest1{
	public static void main (String args[])
    {
    	Car car = null;
        FireEngine fe = new FireEngine();
        fIREEngine fe2 = null;
        
        fe.water();
        car = fe;		//car = (Car)fe;에서 형변환이 생략된 형태다.
      //car.water();	//컴파일에러 Car타입의 참조변수로는 water를 호출할 수 없다.
      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!!!");
    }
}

실행결과
water!!!
water!!!

실행과정

  • Car car = null
    Car타입의 참조변수 car를 선언하고 null로 초기화한다.

  • FireEngine fe = new FireEngine();
    FireEngine인스턴스를 생성하고 FireEngine타입의 참조변수가 참조하도록 한다.

  • car = fe; //조상타입 <- 자손타입
    참조변수 fe가 참조하고 있는 인스턴스를 참조변수 car가 참조하도록 한다. fe값이 car에 저장된다. 이 때 두 참조변수의 타입이 다르므로 참조변서 fe가 형변환되어야 하지만 생략되었다.
    이제는 참조변수 car를 통해서도 FireEngine인스턴스를 사용할 수 있지만, fe와 달리 car는 Car타입이므로 Car클래스의 멤버가 아닌 water()는 사용할 수 없다.

  • fe2 = (FireEngine)car; //자손타입<-조상타입
    참조변수 car가 참조하고 있는 인스턴스를 참조변수 fe2가 참조하도록 한다. 이 때 두 참조변수의 타입이 다르므로 참조변수 car를 형변환하였다. car에는 FireEngine인스턴스의 주소가 저장되어 있으므로 fe2에도 FireEngine인스턴스의 주소가 저장된다.
    이제는 참조변수 fe2를 통해서도 FireEngine인스턴스를 사용할 수 있지만, car와 달리, fe2는 FireEngine타입이므로 FireEngine인스턴스의 모든 멤버들을 사용할 수 있다.

class CastringTest2{
	public static void main(String args[])
    {
    	Car car = new Car();
        Car car2 = null;
        FireEngine fe = null;
        
        car.drive();
        fe = (FireEngine)car; //컴파일은ok, 실행 시 에러 발생
        fe.drive();
        car2 = fe;
        car2.drive();
    }
}

에러가 발생한 이유는 형변환에 오류가 있기 때문이다. 참조변수 car가 참조하고 있는 인스턴스가 Car타입의 인스턴스라는게 문제다. 조상타입의 인스턴스를 자손타입의 참조변수로 참조하는 것은 허용되지 않는다.

컴파일 시에는 참조변수간의 타입만 체크하기 때문에 실행 시 생성될 인스턴스의 타입에 대해서는 전혀 알지 못한다. 그래서 컴파일 시에는 문제가 없었지만, 실행 시에는 에러가 발생하여 실행이 비정상적으로 종료된 것이다.

  • 서로 상속관계에 있는 타입간의 형변환은 양방향으로 자유롭게 수행될 수 있다.
  • 참조변수가 가리키는 인스턴스의 자손타입으로 형변환은 허용되지 않는다.
  • 그래서 참조변수가 가리키는 인스턴스의 타입이 무엇인지 확인하는 것이 중요하다.

intanceof

instanceof를 이용한 연산결과로 true를 얻었다는 것은 참조변수가 검사한 타입으로 형변환이 가능하다는 것을 뜻한다.

void doWork (Car c){
	if (c instanceof FireEngine)
    {
    	FireEngine fe = FireEngine)c;
        fe.water();
    } else if ( c interfaceof Ambulance)
    {
    	Ambulance a = (Ambulance) c;
        a.siren();
    }
}

이 메서드가 호출될 때, 매개변수로 Car클래스 또는 그 자손 클래스의 인스턴스르 ㄹ넘겨받겠지만 메서드 내에서는 정확히 어떤 인스턴스인지 알 길이 없다. 그래서 instanceof 연산자를 통해서 참조변수 c가 가리키고 있는 인스턴스의 타입을 체크하고, 적절히 형변환한 다음에 작업해야한다.

조상타입의 참조변수로는 실제 인스턴스의 멤버들을 모두 사용할 수 없기 때문에, 실제 인스턴스와 같은 타입의 참조변수로 형변환을 해야만 인스턴스의 모든 멤버들을 사용할 수 있다.

class InstanceofTest{
	public static void main(String args []){
    	FireEngine fe = new FireEngine();
        
        if(fe instanceof FireEngine)
        {
        	System.out.println("This is a FireEngine instance.");
        }
        if (fe instanceof Car)
        {
        	System.our.println("This is a Car instanceof.");
        }
        if (fe instanceof Object)
        {
        	System.out.println("This is an Object instance.");
        }
        
        System.out.println(fe.getClass().getName()); //클래스의 이름을 출력
    }
}	//class 

class FireEngine extends Car {}

실행결과

This is a FireEngine instance
This is a Car instance
This is an Object instance
FireEngine

어떤 타입에 대한 instanceof연산의 결과가 true라는 것은 검사한 타입으로 형변환이 가능하다는 것을 뜻한다.

profile
어제 보다는 내일을, 내일 보다는 오늘을 🚀

0개의 댓글