다형성 (Polymorphism)
📚 참고
자바의 정석 기초편 (객체지향개념만)
다형성
- 조상타입 참조변수로 자손타입 객체를 다루는 것
- 여러가지 형태를 가질 수 있는 능력
// 조상 클래스 (멤버 5개)
class Tv {
boolean power;
int channel;
void power() { power = !power }
void channelUp() { ++channel; }
void channelDown() { --channel; }
}
// 자손 클래스 (조상 멤버 + 자신 멤버 = 총 멤버 7개)
class SmartTv extends Tv {
String text; // 캡션(자막)을 보여주기위한 문자열
void caption() {
// 내용생략
}
}
Tv t = new SmartTv(); // 타입 불일치
- 조상타입 참조변수(t)로 자손타입 객체(SmartTv)를 가리킨다.
객체와 참조변수 타입이 일치할 때 / 일치하지 않을 때의 차이
1. 참조변수와 인스턴스의 타입이 일치
SmartTv s = new SmartTv(); // 참조변수와 인스턴스의 타입이 일치
2. 조상타입 참조변수로 자손타입 인스턴스를 참조
Tv t = new SmartTv(); // 조상 타입 참조변수로 자손 타입 인스턴스를 참조
3. 자손타입 참조변수로 조상타입 인스턴스 참조 불가
SmartTv s = new Tv(); // 🚩에러 자손타입 참조변수로 조상타입 인스턴스를 참조
정리
Q. 참조변수의 타입은 인스턴스의 타입과 반드시 일치?
A. NO. 일치하는 것이 보통이지만 일치하지 않을 수도 있다.
Q. 참조변수가 조상 타입일 때와 자손 타입일 때의 차이?
A. 참조변수로 사용할 수 있는 멤버의 갯수가 달라진다.
(위의 예시에서 조상 타입일 땐, 5개. 자손 타입일 땐, 7개)
Q. 자손 타입의 참조변수로 조상 타입의 객체를 가리킬 수 있나?
A. NO. 허용되지 않는다.
참조변수의 형변환
- 사용할 수 있는 멤버의 갯수를 조절하는 것
- 조상 - 자손 관계의 참조변수는 서로 형변환이 가능
// 조상 클래스
class Car {
String color;
int door;
void drive() {
System.out.println("drive! Brrr~");
}
void stop() {
System.out.println("stop!!!");
}
}
// 자손 클래스 1
class FireEngine extends Car {
void water() {
System.out.println("water!");
}
}
// 자손 클래스 2
class Ambulance extends Car {
}
FireEngine f = new FireEngine();
Car c = (Car) f; // OK ➡ 조상인 Car 타입으로 형변환 (생략가능)
FireEngine f2 = (FireEngine) c; // OK ➡ 자손인 FireEngine 타입으로 형변환 (생략불가)
Ambulance a = (Ambulance) f; // 🚩에러 ➡ 상속관계가 아닌 클래스간의 형변환 불가
예제 1
f2.water(); // OK
c.water(); // 🚩에러
- 컴파일 에러 ➡ Car 타입의 참조변수(c)로 FireEngine의 멤버인 water()를 호출할 수 없다.
예제 2
class Ex7_7 {
public static void main(String args[]) {
Car car = null;
FireEngine fe = (FireEngine) car; // 조상 ➡ 자손 형변환
Car car2 = (Car) fe; // 자손 ➡ 조상 형변환
car2.drive(); // 🚩 NullPointerException
}
}
- car2 참조변수에 저장된 값 = null
- 즉, 가리키는 객체가 없는 상태에서 drive 메서드를 호출하니 NullPointerException이 발생한다.
예제 3
class Ex7_7 {
public static void main(String args[]) {
Car c = new Car();
FireEngine fe = (FireEngine) c; // compile OK. 실행 시, 🚩ClassCastException
fe.water(); // compile OK
}
}
- 참조변수들이 가리키고 있는 실제 인스턴스가 무엇인지가 중요 (그 인스턴스의 멤버 갯수가 기준이 됨)
- Car 타입의 인스턴스가 생성되고, 이 인스턴스의 멤버 갯수는 4개
- Car와 FireEngine은 조상 - 자손 관계이므로 형변환이 가능하고 컴파일 시, 에러가 나지않는다.
- 그러나 참조변수 fe는 FireEngine 타입이므로 5개의 멤버를 제어할 수 있는데 fe가 가리키는 실제 인스턴스는 멤버 갯수가 4개인 Car 타입의 인스턴스이다.
- 그래서 실행 시, ClassCastException이 발생하는 것이다.
- 마찬가지로 Car 타입의 인스턴스를 가리키고 있는 fe 참조변수로 water 메서드를 호출할 수 없다.
결론
✅ 참조변수들이 가리키는 실제 인스턴스를 확인하고
✅ 참조변수 타입이 가지고 있는 멤버의 갯수가 실제 인스턴스의 멤버 갯수를 넘어서면 안 된다.
instanceof 연산자
- 참조변수의 형변환 가능여부 확인 ➡ 가능하면 true 반환
- 형변환 전에 반드시 instanceof로 확인해야 함
void doWork(Car c) {
if (c instanceof FireEngine) { // 1. 형변환이 가능한지 확인
FireEngine fe = (FireEngine) c; // 2. 형변환
fe.water();
}
}
// 모두 가능
doWork(new Car());
doWork(new FireEngine());
doWork(new Ambulance());
Car c = new FireEngine();
doWork(c);
- 형변환을 하는 이유는 인스턴스의 원래 기능을 모두 사용하기 위함이다.
- Car 타입의 참조변수 c로 water()를 호출할 수 없으니 FireEngine 타입으로 형변환하여 water()를 호출한다.
FireEngine fe = new FireEngine();
System.out.println(fe instanceof Object); // true
System.out.println(fe instanceof Car); // true
System.out.println(fe instanceof FireEngine); // true
Object obj = (Object) fe; // OK
Car c = (Car) fe; // OK
- 자기자신은 물론이고 조상의 타입으로도 형변환이 가능하다.
정리
Q. 참조변수의 형변환을 하는 이유
A. 참조변수를 변경함으로써 사용할 수 있는 멤버의 갯수를 조절
Q. instanceof 연산자는 언제 사용하는지?
A. 참조변수를 형변환하기 전에 형변환 가능 여부를 확인할 때
다형성의 장점
- 매개변수의 다형성
- 하나의 배열로 여러 종류의 객체 다루기
1. 매개변수의 다형성
- 참조형 매개변수는 메서드 호출 시, 자신과 같은 타입 또는 자손 타입의 인스턴스를 넘겨줄 수 있다.
// 조상 클래스
class Product {
int price; // 제품가격
int bonusPoint; // 보너스점수
}
// 자손 클래스
class Tv extends Product {}
class Computer extends Product {}
class Audio extends Product {}
// 물건사는 사람
class Buyer {
int money = 1000; // 소유금액
int bonusPoint = 0; // 보너스점수
}
Buyer 클래스에 추가될 buy 메서드 작성 방법
1) buy 메서드를 오버로딩하여 제품마다 메서드 만들기
// Tv 구매
void buy(Tv t) {
money -= t.price;
bonusPoint += t.bonusPoint;
}
// 컴퓨터 구매
void buy(Computer c) {
money -= t.price;
bonusPoint += t.bonusPoint;
}
// 오디오 구매
void buy(Audio a) {
money -= t.price;
bonusPoint += t.bonusPoint;
}
2) 조상인 Product 타입으로 매개변수 작성
void buy(Product p) {
money -= p.price;
bonusPoint += p.bonusPoint;
}
// 다형성
Product p1 = new Tv();
Product p2 = new Computer();
Product p3 = new Audio();
// buy 메서드 호출
buy(p1);
buy(p2);
buy(p3);
- buy 메서드 하나로 여러 물건을 살 수 있다.
2. 하나의 배열로 여러 종류의 객체 다루기
- 원래는 하나의 배열에 한 가지 종류의 객체만 저장할 수 있다.
- 조상타입의 배열에 자손들의 객체를 담을 수 있다.
다형성 요약
- 조상타입 참조변수로 자손타입 객체를 다룬다.
- 참조변수의 형변환 (사용할 수 있는 멤버 갯수 조절)
- instanceof 연산자 (형변환 가능 여부)
'JAVA > 객체지향개념' 카테고리의 다른 글
인터페이스 (Interface) (0) | 2021.03.20 |
---|---|
추상 클래스 (Abstract Class), 추상 메서드 (Abstract Method) (0) | 2021.03.20 |
캡슐화💊 (0) | 2021.03.19 |
접근 제어자 (Access Modifier) (0) | 2021.03.18 |
import문, static import문 (0) | 2021.03.18 |
댓글
이 글 공유하기
다른 글
-
인터페이스 (Interface)
인터페이스 (Interface)
2021.03.20 -
추상 클래스 (Abstract Class), 추상 메서드 (Abstract Method)
추상 클래스 (Abstract Class), 추상 메서드 (Abstract Method)
2021.03.20 -
캡슐화💊
캡슐화💊
2021.03.19 -
접근 제어자 (Access Modifier)
접근 제어자 (Access Modifier)
2021.03.18