다운 캐스팅 이해하기

이수찬·2023년 6월 22일
0

다형성 : 조상 클래스 타입의 참조변수로 자손클래스의 인스턴스를 참조할 수 있도록 하는것.

실제 인스턴스가 자손클래스이고, 참조변수가 조상 클래스라면, 참조변수로는 해당 인스턴스의 모든 멤버를 사용할 수 없다.

결국 같은 인스턴스를 참조하더라도, 참조변수의 타입에 따라 사용할 수 있는 멤버의 갯수가 달라질 수 있다. <- 이게 가장 중요!

자손타입을 참조변수로 하여 조상타입의 인스턴스를 참조하는 것은 가능할까?

불가능하다.
실제 인스턴스인 조상 클래스가 사용할 수 있는 멤버보다 참조변수가 사용할 수 있는 멤버가 많기 때문이다.
이유 : 자손타입의 참조변수로 조상타입의 인스턴스를 참조하는 것은 존재하지 않는 멤버를 사용하고자 할 가능성이 있으므로 허용하지 않는 것이다.

형변환

  1. 형변환을 생략할 수 있는 이유
  • 우선 생략은 업캐스팅만 가능하다. (자손 -> 조상)
    : 참조변수가 사용할 수 있는 멤버의 갯수보다 인스턴스가 갖고 있는 멤버의 갯수가 적은 것이 분명하기에 생략 가능

  • 그럼 다운캐스팅은 왜 생략 불가능?
    : 참조변수가 사용할 수 있는 멤버의 갯수를 늘리는 작업이므로,
    실제 인스턴스의 멤버 개수보다 참조 변수가 사용할 수 있는 멤버의 갯수가 많아지는 것이므로 문제 발생 가능.!

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

즉, 다운 캐스팅은 조상클래스의 멤버만 사용가능하다.

다형성이란 :

  • 추상화 : 조상 유형의 참조 변수를 사용하면 하위 클래스의 특정 구현 세부 정보를 추상화하고 조상이 제공하는 공통 기능에 집중할 수 있습니다. 이렇게 하면 코드를 더 모듈화하고 이해하기 쉽게 만들 수 있습니다
  • 캡술화 : 조상 유형의 참조 변수를 사용하면 특정 하위 클래스의 구현 세부 정보를 숨기고 조상 클래스의 인터페이스에서 선언된 멤버로만 액세스를 제한할 수 있습니다.
  • 다형성 동작 : 조상 유형의 참조 변수를 사용할 때 다른 하위 클래스의 개체를 해당 변수에 할당할 수 있습니다. 이를 통해 서로 다른 유형의 개체가 공통 조상을 공유하는 한 교환 가능하게 작동할 수 있는 코드를 작성할 수 있습니다.
  • 코드 유연성 및 확장성 : 조상 유형과 작동하는 코드를 작성할 수 있으며 기존 코드베이스를 수정하지 않고도 생성한 새 하위 클래스와 자동으로 작동합니다.

즉, 다형성은 보다 유연하고, 모듈식이며 확장 가능한 코드를 작성할 수 있도록 도와준다.

업캐스팅 : 참조변수를 통해 자손클래스의 인스턴스를 사용할 수 있지만, 자손클래스의 참조변수와 달리 참조변수가 조상클래스 타입이므로, 자손클래스만의 멤버(ex. water())는 사용할 수 없다.
업캐스팅을 사용하면 서로 다른 하위 클래스의 개체를 공통 조상 클래스의 인스턴스로 처리할 수 있다.

  • 다형성: 업캐스팅은 다형성 동작을 가능하게 합니다. 개체를 공통 조상 유형으로 업캐스팅하면 개체를 균일하게 처리하고 조상 클래스에 정의된 공통 메서드 또는 속성을 호출할 수 있습니다. 이를 통해 서로 다른 하위 클래스의 개체에서 상호 교환적으로 작동하는 코드를 작성할 수 있으므로 코드 유연성과 재사용성이 향상됩니다.
  • 추상화 및 캡슐화: 업캐스팅을 사용하면 파생 클래스의 특정 구현 세부 정보를 추상화하고 조상 클래스에 정의된 공통 기능에 집중할 수 있습니다. 이렇게 하면 파생 클래스의 내부 작업을 숨김으로써 코드 모듈성과 캡슐화가 향상되어 코드를 유지 관리하기 쉽고 이해하기 쉬워집니다.
  • 메서드 재정의: 업캐스팅은 메서드 재정의를 처리할 때 중요한 역할을 합니다. 메서드가 하위 클래스에서 재정의되면 업캐스팅을 통해 슈퍼클래스 유형의 참조 변수를 사용하여 하위 클래스의 재정의된 메서드를 호출할 수 있습니다. 이렇게 하면 하위 클래스에 있는 메서드의 적절한 구현이 런타임에 실행되어 동적 디스패치를 용이하게 하고 다형성 동작을 사용할 수 있습니다.
  • 코드 유연성 및 확장성: 업캐스팅은 코드베이스의 유연성과 확장성을 향상시킵니다. 객체를 업캐스팅하면 슈퍼클래스 유형과 작동하는 코드를 작성할 수 있으며 기존 코드를 수정할 필요 없이 생성한 새 하위클래스와 자동으로 작동합니다. 이는 쉬운 코드 확장을 촉진하고 기존 코드베이스에 영향을 주지 않고 새로운 기능을 추가할 수 있도록 합니다.
  • 호환성 및 호환성 검사: 업캐스팅은 슈퍼클래스 유형을 매개변수로 예상하는 메서드나 인터페이스에 개체를 전달해야 하는 시나리오에서 자주 사용됩니다. 객체를 필수 슈퍼클래스 유형으로 업캐스팅하면 호환성이 보장되고 객체를 적절한 메서드나 인터페이스로 원활하게 전달할 수 있습니다. 또한 업캐스팅을 사용하면 개체가 특정 슈퍼클래스의 인스턴스인지 확인하거나 특정 인터페이스를 구현하는 것과 같은 호환성 검사를 수행할 수 있습니다.

다운캐스팅 : 참조변수가 사용할 수 있는 멤버를 복구한다.
하위 클래스의 멤버에 다시 접근할 수 있도록 해준다!
다운캐스팅을 하기전에 instanceOf로 해당 인스턴스가 하위 클래스인지 확인하고 사용하자!
즉, 하위클래스별 멤버에 다시 접근하는데 사용된다.

주의)

SuperCar superCar = (SuperCar) car;

이런 코드는 컴파일 오류가 발생하지 않지만, 실행 시 ClassCastException이 발생한다.
이유 : 문제는 참조변수 car가 참조하고 있는 인스턴스가 Car라는데이 있다.
위에서 말했듯이 형변환은 참조변수의 타입을 변환하는 것이지, 인스턴스를 변환하는 것이 아니기 때문에 형변환은 인스턴스에 아무런 영향도 미치지 않는다.
즉, 형변환을 통해 참조변수는 SuperCar타입인데, 인스턴스는 Car이므로, 참조변수가 사용할 수 있는 멤버의 갯수가 인스턴스가 사용할 수 있는 멤버의 갯수보다 많아지는 문제가 발생하여 형변환을 할 수 없는 예외가 발생한다.

  • instanceof() : 해당 참조변수가 참조하고 있는 인스턴스의 실제 타입을 알아보기 위해 사용
    -> 연산 결과가 true라는 뜻은 검사한 타입으로 형변환을 해도 문제가 없다는 뜻이다.

다형성의 이유 : 매개변수에 만약 어떤 특정 타입만 들어갈 수 있다고 가정해보자.
이때, 다양한 타입들로 메소드를 사용하고 싶다고 한다면, 해당 메소드의 갯수만큼 만들어야 한다.
이럴때 다형성을 사용하여 메소드 1개로 모든 매개변수를 처리할 수 있도록 만들 수 있다.

인터페이스로도 사용가능하다! (ex> interface : shape, -> 구현체 : circle, Shape circle = context.getBean(Circle.class) ,
circle.calculateArea(); -> 이런거 가능함
물론 해당 멤버변수에는 접근할 수 없다.<인터페이스를 구현한 클래스로는>)

  • 조상클래스의 메소드를 오버라이딩한 후 업캐스팅을 한 후 해당 메소드를 실행하면 자식클래스의 메소드가 실행된다.

그럼 왜 조상타입의 참조변수를 사용해서 인스턴스의 일부 멤버만을 사용하도록 해야할까?

0개의 댓글