KoreanFoodie's Study
7. 자바의 상속 : 타입 변환과 다형성 본문
타입 변환과 다형성 (Polymorphism)
다형성은 같은 타입이지만 실행 결과가 다양한 객체를 이용할 수 있는 성질을 말한다.
자동 타입 변환(Promotion)
자동 타입 변환의 개념은 자식은 부모의 특징과 기능을 상속받기 때문에 부모와 동일하게 취급될 수 있다는 것이다.
// <- 방향으로 자동 타입 변환
부모클래스 변수 = 자식클래스 타입;
Cat cat = new Cat();
Animal animal = cat;
// Animal animal = new Cat();
위 코드로 생성되는 cat과 animal 변수는 타입만 다를 뿐, 동일한 cat 객체를 참조한다.
바로 위의 부모가 아니더라도 상속 계층에서 상위 타입이라면 자동 타입 변환이 일어날 수 있다.
package classes;
class A {}
class B extends A {}
class C extends A {}
class D extends B {}
class E extends C {}
public class PromotionExample {
public static void main(String[] args)
{
B b = new B();
C c = new C();
D d = new D();
E e = new E();
A a1 = b;
A a2 = c;
A a3 = d;
A a4 = e;
B b1 = d;
C c1 = e;
// Compile error! (Not inheritance relationship)
//B b3 = e;
//C c2 = d;
}
}
부모 타입으로 자동 타입 변환된 이후에는 부모 클래스에 선언된 필드와 메소드만 접근이 가능하다. 비록 변수는 자식 객체를 참조하지만 변수로 접근 가능한 멤버는 부모 클래스 멤버로만 한정된다. 그러나 예외가 있는데, 메소드가 자식 클래스에서 오버라이딩 되었다면 자식 클래스의 메소드가 대신 호출된다. 이는 매우 중요한 성질이다!
왜 다형성인가?
그렇다면 왜 이런 귀찮은 성질을 익혀야 하는 걸까? 왜 부모 클래스 타입의 객체는 자식 클래스에서 오버라이딩된 메소드를 실행하게 되는 걸까?
예를 들어, 우리가 Tire라는 부모 클래스를 만들었다고 가정해 보자. 그런데 기술이 점점 발전하거나, 기기가 고장이 나서 타이어를 교체해야 하는 상황이 왔다고 하자. 이때, 만약 부모 타입의 클래스를 상속 받은 자식 객체에서, 더 좋은 기능을 장착한 후(오버라이딩), 타이어를 해당 타이어로 업그레이드할 수 있다면 어떨까?
우리는 이미 Tire라는 기본 클래스의 멤버와 메소드를 알고 있기 때문에, 상속된 자식 객체가 어떤 녀석인지 자세히 알지 못해도, 이미 정의된 메소드를 실행할 수 있다. 즉, 교체와 유지보수가 간편해 지는 것이다!
매개 변수의 다형성
자동 타입 변환은 필드의 값을 대입할 때에도 발생하지만, 주로 메소드를 호출할 때 많이 발생한다. 메소드를 호출할 때에는 매개 변수의 타입과 동일한 매개값을 지정하는 것이 정석이지만, 매개값을 다양화하기 위해 매개 변수에 자식 타입 객체를 지정할 수도 있다.
public class Driver
{
void drive(Vehicle vehicle)
{
vehicle.run();
}
}
drive 메소드는 다음과 같이 호출될 것이다.
Driver drive = new Driver();
Vehicle vehicle = new Vehicle();
drive.drive(vehicle);
만약 Vehicle의 자식 클래스인 Bus 객체를 drive() 메소드의 매개값으로 넘겨준다면 어떻게 될까?
Driver drive = new Driver();
//Vehicle vehicle = new Vehicle();
Bus bus = new Bus();
drive.drive(bus); // 자동 타입 변환 발생 Bus -> Vehicle
자동 타입 변환이 발생한다! 즉, 매개 변수의 타입이 클래스일 경우, 해당 클래스의 객체뿐만 아니라 자식 객체까지도 매개값으로 사용할 수 있다! 매개값으로 어떤 자식 객체가 제공되느냐에 따라 메소드의 실행 결과는 다양해질 수 있다(매개 변수의 다형성). 자식 객체가 부모의 메소드를 재정의(오버라이딩)했다면 메소드 내부에서 오버라이딩된 메소드를 호출함으로써 메소드의 실행 결과는 다양해진다.
강제 타입 변환
강제 타입 변환(Casting)은 부모 타입을 자식 타입으로 변환하는 것을 말한다. 모든 부모 타입을 자식 클래스 타입으로 강제 변환할 수 있는 것은 아니다. 자식 타입이 부모 타입으로 자동 변환한 후, 다시 자식 타입으로 변환할 때 강제 타입 변환을 사용할 수 있다.
public class Parent
{
public String field1;
public void method1()
{
System.out.println("Parent-method1()");
}
public void method2()
{
System.out.println("Parent-method2()");
}
}
public class Child extends Parent
{
public String field2;
public void method3()
{
System.out.println("Child-method3()");
}
}
public class ChildExample
{
public static void main(String[] args)
{
Parent parent = new Child(); // 자동 타입 변환
parent.field1 = "data1";
parent.method1();
parent.method2();
/*
parent.field = "data2"; // (불가능)
parent.method3(); // (불가능)
*/
Child child = (Child) parent; // 강제 타입 변환
child.field2 = "yyy"; // (가능)
child.method3(); // (가능)
}
}
객체 타입 확인 (instanceOf)
강제 타입 변환은 자식 타입이 부모 타입으로 변환되어 있는 상태에서만 가능하기 때문에 다음과 같이 부모 타입의 변수가 부모 객체를 참조할 경우 자식 타입으로 변환할 수 없다.
Parent parent = new Parent();
Child child = (Child) parent; // error
이런 경우, instanceof 연산자를 사용하면 객체의 타입을 확인할 수 있다.
boolean result = 좌항(객체) instanceof 우항(타입)
좌항이 우항의 타입이면 true를, 그렇지 않으면 false를 리턴한다. 따라서 메소드 사용시, 반드시 타입을 확인하고 시도하자!
public void method(Parent parent)
{
if(parent instanceof Child)
{
Child child = (Child) parent;
}
}
final 클래스와 final 메소드
final 키워드는 클래스, 필드, 메소드 선언 시에 사용할 수 있다. 이는 해당 선언이 수정될 수 없음을 의미하는데, 클래스를 선언할 때 final 키워드를 class 앞에 붙이게 되면 이 클래스는 최종적인 클래스이므로 상속할 수 없는 클래스가 된다.
마찬가지로, final로 선언한 method는 Overriding이 불가능하다.
'Tutorials > Java' 카테고리의 다른 글
8-2. 자바 인터페이스 : 인터페이스 상속, 디폴트 메소드와 인터페이스 확장 (0) | 2021.09.15 |
---|---|
8-1. 자바 인터페이스의 역할과 선언, 구현 예제 살펴보기 (0) | 2021.09.15 |
6-3. 어노테이션(Annotation) : 자바 어노테이션 개념과 예제 (0) | 2021.09.14 |
6-2. 패키지 : 자바 패키지의 개념과 예제 (0) | 2021.09.14 |
6-1. 정적 멤버와 static : 정적 메소드, 정적 초기화 블록, 싱글톤 (0) | 2021.09.14 |