1. 다형성
다형성이란 조상 타입의 참조 변수로 자손 타입의 인스턴스를 참조할 수 있다는 것이다.
(1) 조상 타입 참조 변수 = 자손 타입 인스턴스
(2) 자손 타입 참조 변수 ≠ 조상 타입 인스턴스
앞서 참조 변수의 타입과 참조하는 인스턴스의 타입은 같다고 했지만 다형성을 이용하면 위와 같이 참조할 수도 있다. (2)와 같은 반대의 경우는 불가능하다. 참조 변수의 타입과 참조하는 인스턴스의 타입이 같은 경우랑 비교할 때, (1)에서는 참조할 수 있는 멤버의 개수가 줄어든다. 참조 변수가 사용할 수 있는 멤버의 개수 <= 인스턴스의 멤버 개수이다.
1) 참조 변수의 형변환
참조 변수의 형변환은 참조 변수를 다른 종류로 바꾸는 것이고 이를 통해 사용 가능한 멤버의 개수를 조절할 수 있다. 상속 관계, 조상 자손 관계에서만 형변환이 가능하다. 형변환 연산자는 생략 가능한 경우(자손 → 조상)가 있는데 별로 중요하지 않으니 그냥 쓰도록 하자.
[예 1]
class Car {}
class FireEngine extends Car {}
class Ambulance extends Car {}
FireEngine f = new FireEngine();
Car c = (Car) f;
FireEngine f2 = (FireEngine) c;
Ambulance a = (Ambulance) f; // 불가
결국 인스턴스는 아무런 변화가 없고 참조할 수 있는 멤버의 개수만 달라지는 것 뿐이다 (전체 → 노란색 → 전체)
[참고 1]
위의 [예 1]의 코드에서
6번째 줄: 사용 가능한 멤버 수 5 → 4개 (안전 O) => 캐스트 연산자 생략 가능 (자손 → 조상)
7번째 줄: 사용 가능한 멤버 수 4 → 5개 (안전 X) => 캐스트 연산자 생략 불가 (조상 → 자손)
[참고 2]
class Car {}
class FireEngine extends Car {}
class Ambulance extends Car {}
Car c = new Car();
FireEngine fe = (FireEngine) c;
fe.water() // 에러 발생
다음은 컴파일은 되지만 마지막 줄에서 에러가 발생한다. fe가 참조하는 인스턴스의 타입이 Car인데 이 인스턴스에는 water() 메서드가 없기 때문이다. 중요한 것은 참조 변수가 참조하는 인스턴스이다. 정리하면 참조 변수의 형변환은 참조 변수의 사용할 수 있는 멤버 개수를 조절하기 위해 필요한 것일 뿐이다.
[참고 1] 멤버 변수가 조상, 자손에 중복으로 정의돼 있을 때, 조상 타입 참조 변수를 사용하면 조상 타입의, 자손 타입 참조 변수를 사용하면 자손 타입의 멤버 변수가 사용됨을 알아두자.
2) instanceof 연산자
다음과 같은 형태로 사용하며 형변환 가능 여부를 확인하는데 사용한다. 즉, 자손 조상 간의 관계를 파악하기 위함이다.
참조 변수 instanceof 타입
형변환 전에는 반드시 instanceof 연산자로 반드시 형변환 가능 여부를 확인해야 한다.
3) 참조 변수와 인스턴스의 연결
class BindingTest {
public static void main(String[] args) {
Parent p = new Child();
Child c = new Child();
System.out.println("p.x = " + p.x);
p.method();
System.out.println("c.x = " + c.x);
c.method();
}
}
class Parent {
int x = 100;
void method() {
System.out.println("Parent Method");
}
}
class Child extends Parent {
int x = 200;
void method() {
System.out.println("Child Method");
}
}
```
결과
100
Child Method
200
Child Method
```
메서드의 경우 참조 변수의 타입이 조상이든 자손이든 실제 메서드(오버라이딩 메서드)가 호출되지만, 변수의 경우 참조 변수에 따라 달라진다. 위 코드를 그림으로 그리면 다음과 같다.
2. 다형성의 장점 2가지
(1) 다형적 매개변수
(2) 한 배열에 여러 종류의 객체 담기
(1)은 자손 타입에 새로운 메서드가 필요할 때 오버로딩(buy(Computer c), buy(Monitor m) => buy(Product p))을 안 하게 해준다는 장점이 있고, (2)에서 배열의 타입을 결정할 때는 클래스들의 가장 가까운 조상 타입으로 설정해주면 된다.
[참고] 모든 종류의 객체를 저장가능하게 하는 배열은 Vector 클래스이고 이 클래스는 멤버로 Object 배열을 갖고 있다.
Q. 참조 변수 타입과 인스턴스 타입은 반드시 일치해야 하는가?
A. 반드시 일치해야 하는 것은 아니며 참조 변수 타입이 인스턴스 타입의 조상이면 상관 없다.
Q. 참조 변수의 타입이 조상 또는 자손일 경우 차이는 무엇인가?
A. 조상일 경우 사용할 수 있는 인스턴스의 멤버 개수가 줄어든다.
Q. 자손 타입의 참조 변수로 조상 인스턴스 참조 가능한가?
A. 불가하다.
Q. 참조 변수를 형변환하는 이유는 무엇인가?
A.
사용 멤버 개수 조절 O
객체 변경 X
객체 주소값 변경 X
=> 타입만 일치시키는 것이다.
Q. instanceof 형변환 연산자를 사용하는 이유는 무엇인가?
A. 참조 변수를 형변환 하기 전 형변환이 가능한지 확인하기 위함이다.
'자바 (Java) > 요약' 카테고리의 다른 글
Chpt 7. 객체 지향 프로그래밍 2 - 주제 6. 내부 클래스 (0) | 2023.10.28 |
---|---|
Chpt 7. 객체 지향 프로그래밍 2 - 주제 5. 추상클래스와 인터페이스 (0) | 2023.10.27 |
Chpt 7. 객체 지향 프로그래밍 2 - 주제 3. 제어자 (0) | 2023.10.26 |
Chpt 7. 객체 지향 프로그래밍 2 - 주제 2. 오버라이딩, 참조변수 super, 생성자 super() (0) | 2023.10.26 |
Chpt 7. 객체 지향 프로그래밍 2 - 주제 1. 클래스 간의 관계 - 상속, 포함 (0) | 2023.10.25 |