9. 다형성
조상 타입 참조 변수로 자손 타입 객체를 다루는 것
SmartTv s = new SmartTv(); // 참조 변수와 인스턴스의 타입이 일치
Tv t = new SmartTv(); // 조상 타입 참조 변수로 자손 타입 인스턴스 참조 -> 다형성
위의 두 선언의 차이는?
일반TV는 멤버 5개를 가지고 있다.
스마트TV는 일반TV를 상속해서 일반TV꺼 5개 + 본인꺼 2개 총 7개를 가지고 있다.
첫 번째 선언은 스마트TV 리모컨으로 스마트TV의 7가지 기능을 모두 사용할 수 있다.
두 번째 선언은 일반TV 리모컨으로 스마트TV의 7가지 기능 중, 일반TV가 가진 5가지 기능만 사용할 수 있다.
더 적은 멤버를 사용 가능한데 왜! 다형성이 장점이 될 수 있을까?
반대로, 자손 타입 참조 변수로는 조상 타입의 객체를 다룰 수 없다
SmartTv s = new Tv(); // 에러
왜 에러가 날까?
스마트TV는 7개의 멤버를 가지고 있다. (일반TV는 5개의 멤버)
예를 들어, 스마트TV 리모컨에는 일반TV에 없는 넷플릭스 기능을 가지고 있는거야.
고객이 스마트TV 리모컨으로 넷플릭스를 보려고 그 버튼을 눌렀어.
근데 에러가 뜨는 거야! 왜?
넷플릭스 버튼이 리모컨에는 있지만 실제 참조하는 객체는 일반TV라서 넷플릭스 기능을 가지고 있지 않거든.
그래서 자식으로는 조상을 다룰 수 없다!
10. 참조변수의 형변환
cf) 기본형의 형변환은 값이 바뀐다.
3.6 -> int형으로 변환 -> 3
★★조상과 자손의 참조변수를 형변환을 한다는 것은
'사용할 수 있는 멤버의 개수를 줄였다 늘였다 마음대로 조절하는 것'★★
조상과 자손 관계의 참조변수는 서로 형변환이 가능하다.
왜 형변환 한다고? 사용할 수 있는 멤버를 줄였다가 늘였다가 하려고!
class Car { }
class FireEngine extends Car { }
class Ambulance extends Car { }
FireEngine f = new FireEngine();
Car c = (Car)f; // 조상인 Car 타입으로 형변환
FireEngine f2 = (FireEngine)c; // 자손인 FireEngine 타입으로 형변환
Ambulance a = (Ambulance)f; // 에러. 상속관계가 아닌 클래스 간의 형변환 불가
참조변수의 형변환은 그저 리모컨을 다른 종류의 것으로 바꾸는 것뿐이다.
리모컨의 타입을 바꾸는 이유는 사용할 수 있는 멤버 개수를 조절하기 위한 것이고,
public class Main {
public static void main(String[] args) {
Car car = null;
FireEngine fe = new FireEngine();
FireEngine fe2 = null;
fe.water(); // water-
car = (Car)fe; // car = fe; -> 형변환 생략 가능 -> 왜? 조상 타입으로 자손 타입 다루는건 안전
//car.water(); // Car 타입으로는 자식을 다룰 수 없음
fe2 = (FireEngine)car; // 형변환 생략 불가 -> 왜? 자손타입으로 조상 타입 다루는건 위험하기 때문
fe2.water(); // water-
}
}
class Car {
String color;
int door;
void drive() {
System.out.println("drive-");
}
void stop() {
System.out.println("stop-");
}
}
class FireEngine extends Car {
void water() {
System.out.println("water-");
}
}
참조변수 fe로는 5개의 멤버 사용 가능, 참조변수 car로는 4개의 멤버 사용 가능, 참조변수 fe2로는 5개의 멤버 사용 가능
조상과 자식 간에는 아주 유연하고 자유롭게 형변환이 가능하다는 것을 알 수 있지!
**주의!
객체가 없어도 형변환에는 문제가 없다. 심지어 호출도 가능하다.
하지만 실행하면 에러가 발생한다.
컴파일러는 객체가 있든지 말든지 형변환만 맞게 해 주면 깜빡 속는다. 그래서 컴파일 에러가 안 난다. (바보)
즉, 형변환에서는 실제 인스턴스가 무엇인지가 중요하다.
(= 실제 가리키고 있는 객체가 무엇인지가 중요하다 = 가리키는 객체가 new 되어있는지가 중요하다)
public class Main {
public static void main(String[] args) {
Car c = new Car();
FireEngine fe = (FireEngine)c; // 형변환 실행 에러. 왜? 실제 가리키는 객체가 Car잖아.
fe.water(); // 컴파일 OK. 하지만 실행하면 ClassCastException 발생.
}
}
10. instanceof 연산자
참조변수의 형변환 가능 여부 확인에 사용
형변환하기 전에는 반드시 instanceof로 꼭 형변환 가능 여부 확인
// 매개변수가 Car 타입으로 Car, FireEngine, Ambulance 모두 들어올 수 있음
void doWork(Car c) {
if(c instanceof FireEngine) { // c가 FireEngine 타입이니? (Car 혹은 FireEngine이니?)
FireEngine fe = (FireEngine)c; // 들어온 타입을 FireEngine으로 형변환
fe.water();
}
else if(c instanceof Ambulance) {
Ambulance a = (Ambulance)c;
}
}
자손과 조상 간에는 쌍방으로 형변환이 가능하다. 따라서 instanceof의 값이 true가 나온다.
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
11. 다형성의 장점 - 매개변수의 다형성
★★★★★정말 중요, 반복해서 이해하자.★★★★★
메서드의 매개변수로 조상 타입의 참조변수를 사용해서
하나의 메서드로 여러 타입의 객체(자손)를 받는 것
이게 무슨 말이냐? 아래 예제를 살펴보자.
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;
}
// 다형성이 없다면~
// 상품을 구매할 때마다 하나씩 다 메서드를 만들어줘야함
void buy(Tv t) {
money -= p.price;
bonusPoint += p.bonusPoint;
}
void buy(Computer c) {
money -= p.price;
bonusPoint += p.bonusPoint;
}
void buy(Audio a) {
money -= p.price;
bonusPoint += p.bonusPoint;
}
// 다형성이 있다면~
// 조상 타입으로 모든 자식 타입을 다룰 수 있게됨
void buy(Product p) { // 매개변수에 Tv, Computer, Audio, Phone, Tablet... 모두 가능
money -= p.price;
bonusPoint += p.bonusPoint;
}
그렇다면 이번에는 실제 예제를 살펴보자.
package java7;
class Product {
int price;
int bonusPoint;
Product(int price) {
this.price = price;
bonusPoint = (int)(price/10.0);
}
}
class Tv extends Product {
Tv(int price) {
super(price);
}
public String toString() {
return "Tv";
}
}
class Computer extends Product {
Computer(int price) {
super(price);
}
public String toString() {
return "Computer";
}
}
class Buyer {
int money = 1000;
int bonusPoint = 0;
// Product를 상속한 클래스는 모두 들어올 수 있으며 이 안에서만 p라는 변수로 다뤄짐
void buy(Product p) {
if(money < p.price) {
System.out.println("잔액 부족");
return;
}
money -= p.price;
bonusPoint += p.bonusPoint;
System.out.println(p + " 구매 완료");
}
}
public class Main {
public static void main(String[] args) {
// Buyer b = new Buyer();
// b.buy(new Tv());
// b.buy(new Computer());
Buyer b = new Buyer();
Tv t = new Tv(100); // Product t = new Tv(100)도 가능
Computer c = new Computer(200); // Product t = new Computer(200)도 가능
// 조상 타입으로 만들어진 메서드를 자식들이 사용하는 것을 볼 수 있음!
b.buy(t); // Tv 구매 완료
b.buy(c); // Computer 구매 완료
System.out.println("현재 잔액: " + b.money); // 현재 잔액: 700
System.out.println("현재 포인트: " + b.bonusPoint); // 현재 포인트: 30
}
}
12. 다형성의 장점 - 하나의 배열에 여러 종류의 객체 다루기
★★★★★정말 중요, 반복해서 이해하자.★★★★★
하나의 배열에 여러 종류의 객체를 저장할 수 있다.
이게 무슨 말이냐?
Product 타입의 배열이 있다. 그 배열에는 Tv 타입, Computer 타입, Audio 타입 모두 들어갈 수 있다.
즉, Product 타입에 Product 타입만 들어올 수 있는 것이 아니라
Product를 상속한 자손들(Tv, Computer, Audio)은 모두 들어올 수 있다는 뜻이다.
Product[] p = new Product[3];
p[0] = new Tv();
p[1] = new Computer();
p[2] = new Audio();
class Product {
int price;
int bonusPoint;
Product(int price) {
this.price = price;
bonusPoint = (int)(price/10.0);
}
}
class Tv extends Product {
Tv(int price) {
super(price);
}
public String toString() {
return "Tv";
}
}
class Computer extends Product {
Computer(int price) {
super(price);
}
public String toString() {
return "Computer";
}
}
class Audio extends Product {
Audio(int price) {
super(price);
}
public String toString() {
return "Audio";
}
}
class Buyer {
int money = 1000;
int bonusPoint = 0;
Product[] cart = new Product[10];
int i = 0; // 물건의 개수
void buy(Product p) {
if(money < p.price) {
System.out.println("잔액 부족");
return;
}
money -= p.price;
bonusPoint += p.bonusPoint;
cart[i++] = p;
System.out.println(p + " 구매 완료");
}
void summary() {
int sum = 0;
String itemList = "";
for(int i = 0; i < cart.length; i++) {
if(cart[i] == null) break;
sum += cart[i].price;
itemList += cart[i] + ", ";
}
System.out.println("구매 금액: " + sum);
System.out.println("구입 제품: " + itemList);
}
}
public class Main {
public static void main(String[] args) {
Buyer b = new Buyer();
b.buy(new Tv(100));
b.buy(new Computer(200));
b.buy(new Audio(50));
b.summary();
}
}
/*
Tv 구매 완료
Computer 구매 완료
Audio 구매 완료
구매 금액: 350
구입 제품: Tv, Computer, Audio,
*/
'JAVA' 카테고리의 다른 글
7장_3 (0) | 2021.01.22 |
---|---|
7장_1 (0) | 2021.01.22 |
쓰레드 (0) | 2021.01.20 |
다형성 / 동적 바인딩 / 인터페이스 / 추상클래스 (0) | 2021.01.18 |
static은 언제 붙일까? (0) | 2021.01.14 |