Java/Basic

다형성과 캐스팅

o3oppp 2024. 12. 30. 21:06
다형적 참조
public class Parent {
      public void parentMethod() {
          System.out.println("Parent.parentMethod");
    } 
}
public class Child extends Parent {
      public void childMethod() {
          System.out.println("Child.childMethod");
    } 
}
 public class PolyMain {
     public static void main(String[] args) {
         Parent poly = new Child(); // 다형적 참조
         poly.parentMethod();
    }
}
  • 부모 타입의 변수가 자식 인스턴스를 참조(Parent poly = new Child())
  • 이 경우 자식 타입인 Child를 생성했기 때문에 메모리 상에 Child와 Parent가 모두 생성
  • 생성된 참조값을 Parent 타입의 변수인 poly에 담아둠
  • 반대로 자식 타입은 부모 타입을 담을 수 없음

다형적 참조와 인스턴스 실행

다형적 참조(ref.인프런)

  • poly.parentMethod()를 호출하면 먼저 참조값을 사용해서 인스턴스를 찾음
  • 다음으로 인스턴스 안에서 실행할 타입도 찾음
  • 위의 경우 Parent 클래스부터 시작해서 필요한 기능을 찾음
  • 인스턴스의 Parent 클래스에 parentMethod()를 호출

다형적 참조의 한계

다형적 참조의 한계(ref.인프런)

  • 호출자인 poly는 Parent 타입이므로 Parent 클래스부터 시작해서 필요한 기능을 찾음
  • 그런데 상속 관계는 부모 방향으로 찾아 올라갈 수는 있지만 자식 방향으로 찾아 내려갈 수는 없음
  • Parent는 부모 타입이고 상위에 부모가 없기때문에 childMethod()를 찾을 수 없으므로 컴파일 오류 발생

다형성과 캐스팅
Child child = (Child) poly // Parent poly
  • (타입) 처럼 괄호와 그 사이에 타입을 지정하면 참조 대상을 특정 타입으로 변경 가능
  • 특정 타입으로 변경하는 것을 캐스팅이라고 함

1. 다운캐스팅

public class TestMain {
	public static void main(String[] args) {
        //부모 변수가 자식 인스턴스 참조(다형적 참조)
        Parent poly = new Child();
        //단 자식의 기능은 호출할 수 없다. 컴파일 오류 발생
        //poly.childMethod();
		
        //다운캐스팅 - 다운캐스팅 결과를 변수에 담아두는 과정이 필요
        Child child = (Child) poly;
        child.childMethod();
        
        //일시적 다운캐스팅 - 해당 메서드를 호출하는 순간만 다운캐스팅
         ((Child) poly).childMethod();
      }
}

다운 캐스팅(ref.인프런)
일시적 다운 캐스팅(ref.인프런)

  • 자식 타입으로 변경하는 것을 다운캐스팅
  • 캐스팅을 한다고 해서 Parent poly의 타입이 변하는 것은 아님
  • 해당 참조값을 꺼내고 꺼낸 참조값이 Child 타입이 되는 것
  • 따라서 poly의 타입은 Parent로 그대로 유지
  • 일시적 다운캐스팅을 사용하면 별도의 변수 없이 인스턴스의 자식 타입의 기능을 사용할 수 있음

2. 업캐스팅

public class TestMain {
      public static void main(String[] args) {
      Child child = new Child();
      Parent parent1 = (Parent) child; //업캐스팅은 생략 가능, 생략 권장
      Parent parent2 = child; //업캐스팅 생략
      
      parent1.parentMethod();
      parent2.parentMethod();
      }
}
  • 부모 타입으로 변경하는 것을 업캐스팅
  • 업캐스팅은 생략 가능
  • 자바에서 부모는 자식을 담을 수 있음

다운캐스팅 주의점
public class TestMain {
	public static void main(String[] args) {
    	Parent parent1 = new Child();
        Child child1 = (Child) parent1;
        child1.childMethod();
        
        Parent parent2 = new Parent();
        Child child2 = (Child) parent2; // 런타임 오류 - ClassCastException
        child2.childMethod(); // 실행 불가
	}
}
  • new Parent()로 부모 타입으로 객체를 생성. 따라서 메모리 상에 자식 타입은 존재하지 않음
  • parent2를 Child 타입으로 다운캐스팅 하지만 메모리 상에 Child는 존재하지 않아 사용 불가
  • 사용할 수 없는 타입으로 다운캐스팅하는 경우에 ClassCastException 예외 발생

다운캐스팅이 가능한 경우 메모리 구조

다운캐스팅이 불가능한 경우 메모리 구조

업캐스팅과의 차이

 

업캐스팅(ref.인프런)

  • 업캐스팅의 경우 객체 생성 시 해당 타입의 상위 부모 타입은 모두 함께 생성
  • 따라서 위로만 타입을 변경하는 업캐스팅은 메모리 상에 인스턴스가 모두 존재하기 때문에 안전
    • new C()로 인스턴스를 생성하면 인스턴스 내부에 자신과 부모인 A, B, C 모두 생성
    • A a = new C() : A로 업캐스팅
    • B b = new C() : B로 업캐스팅
    • C c = new C() : 자신과 같은 타입

다운캐스팅(ref.인프런)

  • 객체를 생성하면 부모 타입은 모두 함께 생성되지만 자식 타입은 생성되지 않기 때문에 다운캐스팅의 경우 인스턴스에 존재하지 않는 하위 타입으로 캐스팅하는 경우 문제 발생
    • new B()로 인스턴스를 생성하면 인스턴스 내부에 자신과 부모인 A, B 모두 생성
    • A a = new B() : A로 업캐스팅
    • B b = new B() : 자신과 같은 타입
    • C c = new B() : 하위 타입 대입 불가, 컴파일 오류
    • C c = (C) new B() : 강제 다운캐스팅, 하지만 B 인스턴스에 C와 관련된 부분이 없으므로 ClassCastException 런타임 오류 발생