자바의 정석 기초편 - 객체지향개념 공부(5)

인영·2022년 11월 4일
0

다형성(polymorphism)

여러 가지 형태를 가질 수 있는 능력
조상 타입 참조 변수로 자손 타입 객체를 다루는 것(타입 일치가 보통이지만 불일치도 가능)

Tv t = new Tv(); // 참조 변수와 인스턴스의 타입이 일치(Tv = Tv())
SmartTv s = new SmartTv(); // 참조 변수와 인스턴스의 타입이 일치(SmartTv = SmartTv())

Tv t = new SmartTv();; // 조상 타입 참조변수로 자손 타입 인스턴스 참조(타입 불일치->다형성)

객체와 참조변수의 타입이 일치할 때와 일치하지 않을 때의 차이?
참조변수와 객체의 타입이 일치하는 경우, 객체의 모든 멤버를 다룰 수 있음
조상 객체로 자손 객체를 다루는 경우, 조상이 가진 멤버만을 다룰 수 있음(자손객체의 기능을 다 사용할 수 없음)

❌ 자손 타입의 참조변수로 조상 타입의 객체를 가리킬 수 없다. (자손->조상 불가능)
자손 타입의 멤버변수는 조상 타입의 멤버변수의 개수와 같거나, 많기 때문에 허용되지 않는다.
자손이 다룰 수 있는 기능보다 부모가 가진 멤버가 적기 때문에 참조변수가 객체에 존재하는 않는 기능을 호출하는 경우 에러 발생

Tv t = new SmartTv(); // OK. 허용 (Tv-조상, SmartTv()-자손)
SmartTv s = new Tv(); // 에러! 허용 안 됨 (SmartTv-자손, Tv()-조상)
🔍 참조변수의 타입은 인스턴스 타입과 반드시 일치해야 하나요?
⭐️ 아니다. 일치하는 것이 보통이지만 일치하지 않을 수도 있다.

🔍 참조변수가 조상타입일 때와 자손타입일 때의 차이?
⭐️ 참조변수로 사용할 수 있는 멤버의 갯수가 달라진다.

🔍 자손 타입의 참조변수로 조상 타입의 객체를 가리킬 수 있나요?
⭐️ 아니다. 허용되지 않는다.

참조변수의 형변환

사용할 수 있는 멤버의 개수를 조절하는 것
조상, 자손 관계의 참조변수는 서로 형변환 가능 (형제끼리는 불가)

class Car {
	String color;
    int door;
    void drvie() { } 
    void stop() { }
}
class FireEngine extends Car {
	void water() { }
}
FireEngine f = new FireEngine(); // 타입 일치, 가리키는 멤버의 개수 5개
Car c = (car)f; // OK. 조상인 Cart타입으로 형변환(생략가능), 가리키는 멤버의 개수 4개
FireEngine f2 = (FireEngine)c; // OK. 자손인 FireEngine타입으로 형변환(생략불가), 가리키는 멤버의 개수 5개
Ambulance a = (Ambulance)f; // 에러! 상속관계가 아닌 클래스 간의 형변환 불가

조상타입으로 형변환 = 범위 내에서 사용 가능한 기능 개수 감소 = 안전 = 형변환 생략 가능
자손타입으로 형변환 = 사용 가능 기능이 늘어남 = 불안전 = 형변환 생략 불가
참조변수 형변환시 참조변수가 가르키는 실제 객체가 중요!!
ex) 실제 객체가 가지고 있는 멤버를 벗어나면?
--> 형변환 실행 에러 ClassCastException


instanceof 연산자

참조변수의 형변환 가능여부 확인에 사용. 가능하면 true 반환
형변환 전에 반드시 instanceof로 확인해야 함
순서 => 1. 확인(형변환을 해도되는지) -> instancof 연산자 사용 2. 형변환

void doWork(Car c){
	// doWork(new Car()); / doWork(new FireEngine()); / doWork(new Ambulance()); 3문장 모두 가능 => Car 또는 Car의 모든 자손이 가능
    // doWork(new FireEngine());은 Car c = new FireEngine(); doWork(c); 와 동일
    
	if(c instanceof FireEngine) { // 1. 형변환이 가능한지 확인
    	FireEngine fe = (FireEngine)c; // 2. 형변환
        // 형변환을 하는 이유는 인스턴스의 원래 기능을 모두사용하려고.
        fe.water();
        // Car 타입의 리모콘인 c로는 water()를 호출할 수 없으니까.
        // 리모콘은 FireEngine타입으로 바꿔서 water()를 호출
    }
}

객체의 조상에 대해서도 참 반환
Object(최고 조상) - Car(자손) - FireEngine(자손)
fe가 FireEngine 객체를 가리키는 객체가 Object? Car? FireEngine?
FireEngine의 조상에 대해서도 true가 나옴
But, instanceof Object가 true가 나왔다고해서 FireEngine가 가리키는 객체가 Object는 아님, 조상이라서 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

Object obj = (Object)fe // OK
Car c = (Car)fe // OK
🔍 참조변수의 형변환은 왜 하나요?
⭐️ 참조변수(리모콘)을 변경함으로써 사용할 수 있는 멤버의 개수를 조절하기 위해서

🔍 instanceof 연산자는 언제 사용하나요?
⭐️ 참조변수를 형변환하기 전에 형변환 가능여부를 확인할 때

매개변수의 다형성

장점 1. 다형적 매개변수 2. 하나의 배열로 여러종류 객체 다루기

다형적 매개변수

참조형 매개변수는 메서드 호출 시, 자신과 같은 타입 또는 자손타입의 인스턴스를 넘겨줄 수 있음

Product(부모) - Tv(자식) 관계이고
class Product { // 부모
	int price;
    int bonusPoint;
}
class Tv extends Product {} // 자손
Buyer 클래스에  void buy(Product p)라는 메서드가 정의되어 있을 때
class Buyer {
	int money = 1000;
    bonusPoint = 0;
    
    void buy(Product p){
		money -+ p.price;
		bonusPoint += p.bounsPoint;
    }
}	
	Buyer b = new Buyer();
    Product p = new Tv1();
    b.buy(p);
    // b.but(new T1()); 위의 두 줄과 같은 코드

여러 종류의 객체를 배열로 다루기

조상타입의 배열에 자손들의 객체를 담을 수 있음.
(하나의 배열에는 같은 타입밖에 저장이 안 되는데 다형성을 이용하면 하나의 배열에 여러 종류 객체를 저장할 수 있음)

Product p1 = new Tv();
Product p2 = new Computer();
Product p3 = new Audio();

위 코드를 아래처럼 하나의 배열에 담기
하나의 배열 p에는 여러 종류의 객체가 담김

Product p[] = new Product[3];
p[0] = new Tv();
p[1] = new Computer();
p[2] = new Audio();

0개의 댓글