본문 바로가기

언어공부/Java

자바 다형성, 참조변수의 형변환, 업캐스팅, 다운캐스팅, instance of

자바 다형성에 대해서 알아보자.

다형성(polymorphism)

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

 

이전까지 우리는 인스턴스의 타입과 일치하는 타입의 참조변수만을 사용했다. 즉 Tv인스턴스를 다루기 위해서는 Tv타입의 참조변수를 사용하고, SmartTv인스턴스를 다루기 위해서는 SmartTv타입의 참조변수를 사용했다. 

ex) Tv t= new Tv(); SmartTv s= new SmartTv(); (class SmartTv extends Tv)

 

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

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

 

여기서 t2와 s 모두 SmartTv를 참조하지만, t2는 SmartTv인스턴스의 모든 멤버를 사용할 수 없다. SmartTv인스턴스 중에서 Tv클래스의 멤버들(상속받은 멤버 포함)만 사용할 수 있다. 둘 다 같은타입의 인스턴스지만 참조변수의 타입에 따라 사용할 수 있는 멤버의 개수가 달라진다.

 

반대로 자손타입의 참조변수로 조상타입의 인스턴스를 참조하는 것은 에러가 발생한다.

ex) SmartTv s2= new Tv(); //에러. 허용 안됨.

 

에러가 발생하는 이유는 실제 인스턴스인 Tv의 멤버개수가 참조변수 s2가 사용할 수 있는 멤버 개수가 더 많을 수 있기 때문에 허용하지 않는다. (자식클래스가 부모클래스로부터 상속되기 때문에 부모클래스보다 자식클래스가 더 많은 멤버개수를 갖고있다.)

 

자세한 내용은 조상타입의 참조변수로 자손 인스턴스를 참조(업캐스팅)한 것을 다시 자손타입의 참조변수에 조상 인스턴스를 참조로(다운캐스팅)하는 참조변수의 형변환인 업캐스팅, 다운캐스팅에서 알아보자. 

ex) Tv t= new Tv(); SmartTv s= new SmartTv(); (class SmartTv extends Tv)

t=s;(업캐스팅) s=(SmartTv)t;(다운캐스팅)

 


참조변수의 형변환

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

 


업캐스팅

업캐스팅은 상속관계에서 자손타입의 참조변수가 조상타입의 참조변수로 변환하는 것을 말한다. 또 서브(하위)클래스의 레퍼런스 값이 슈퍼(상위)클래스의 래퍼런스변수에 저장되면서 슈퍼(상위)클래스 형으로 변환이 일어나는 것을 말한다. 이러한 형변환은 자바 컴파일러에 의해서 암시적으로 형변환이 일어난다. (자손이 부모보다 멤버가 많기 때문에 형변환해도 안전하기 때문이다.)

 

부모클래스와 상속받은 자식 클래스. p01은 자식클래스에서 오버라이딩 됐다.

 

메인메소드에서 1번 p.p01()과 2번 p.p01()은 다른 결과가 출력된다.

 

출력화면

 

업캐스팅을 하면 호출을 할때 자식클래스에서 오버라이딩된 메소드를 호출하게 된다. 실제 가리키는건 자식의 객체지만, 부모의 멤버만 사용할 수 있으며, 메소드는 객체타입이 자식이기 때문에 오버라이딩 된 메소드가 호출하게 된다. 

 


다운캐스팅

다운캐스팅은 상속관계에서 조상타입의 참조변수가 자손타입의 참조변수로 변환하는 것을 말한다.

다운캐스팅의 전제 조건으로 다운캐스팅을 하기 전 먼저 업캐스팅을 해야하며(업캐스팅을 한 것에 대해서만 다운캐스팅을 허용), 캐스팅 연산자를 사용하여 명시적인 다운캐스팅을 해야한다(부모가 자손보다 멤버가 적기 때문에 멤버 손실이 있을 수 있기 때문.).

 

업캐스팅을 한것에 대해서만 다운캐스팅이 가능하다.

 

다운캐스팅은 업캐스팅 한것에 대해서만 다운캐스팅이 가능하며, 다운캐스팅을 할때는 (FireEngine)으로 타입을 명시적으로 기재해줘야 한다. 다운캐스팅한 fe는 자식클래스의 모든 멤버를 사용할 수 있다.

 

다른 클래스에서 업캐스팅을 하지 않고 다운캐스팅을 하면 예외 오류가 발생.

 

만약 업캐스팅을 하지 않고 다운캐스팅을 먼저 하게되면 실행 시 에러가 발생한다 (Exception in thread "main" java.lang.ClassCastException: class Car06 cannot be cast to class FireEngine (Car06 and FireEngine are in unnamed module of loader 'app') at UpCast07.main(UpCast07.java:6))

 


Instanceof 연산자

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

 

 

출력화면

 

여기서 if조건문에서 false가 나오는 부분은 2 번째 if문인 if(car instanceof FireEngine) 이다. 업캐스팅을 안한 상태에서 다운캐스팅을 하려고 했기 때문에 false다. 하지만 3 번째에서 업캐스팅을 한 후 5 번째에서 다운캐스팅을 하려고 하면 true가 나온다.