다형성(polymorphism)
다형성이란?
- 조상 클래스 타입의 참조변수로 자손클래스의 인스턴스를 참조할 수 있도록 한다.
- 자손타입의 참조변수로 조상타입의 인스턴스를 참조할 수는 없다.
class Tv {
boolean power;
int channel;
void power () { /* 내용생략*/ }
void channelUp () { /* 내용생략*/ }
void channelDown () { /* 내용생략*/ }
}
class CaptionTv extends Tv {
String text;
void caption() { /* 내용생략*/ }
}
- 참조변수가 사용할 수 있는 멤버의 개수는 인스턴스의 멤버 개수보다 같거나 적어야 한다. → [자바의 정석 p355]
- Tv t = new CaptionTv ();
- CaptionTv c = new CaptionTv ();
- 참조변수 = 리모콘, 인스턴스 = 기능 → 리모콘이 가진 기능이 더 크면 작동이 안된다고 생각하기 !
참조변수의 형변환
자손타입 → 조상타입 (UpCasting) : 형변환 생략 가능
- 부모 타입으로 업 캐스팅된 이후에는 부모 클래스에 선언된 필드와 메소드만 접근이 가능하다.
- 단, 예외가 있는데 부모 타입의 메소드가 오버라이딩되었다면 오버라이딩된 메소드가 대신 호출된다.
자손타입 ← 조상타입(DownCasting) : 형변환 생략 불가
- 부모 클래스 타입 참조 변수가 실제로 참조하는 객체를 확인하지 않고 강제 형 변환을 시도하면 ClassCastException 예외가 발생할 수 있다.
- 객체가 어떤 클래스의 인스턴스인지 instanceof 연산자를 사용해서 확인할 수 있다.
- 형변환은 참조변수의 타입을 변환하는 것이지 인스턴스를 변환하는 것은 아니기 때문에 참조변수의 형변환은 인스턴스에 영향 X
- 참조변수의 형변환을 통해서, 참조하고 있는 인스턴스에서 사용할 수 있는 멤버의 범위(개수)를 조절하는 것이다.
// 1. 부모 타입의 참조 변수로 부모 객체를 다루는 경우
System.out.println("1. 부모 타입의 참조 변수로 부모 객체를 다루는 경우");
Product product = new Product(); // OK
System.out.println();
// product 참조 변수로 Product의 멤버만 접근 가능.
System.out.println("<product 참조 변수로 Product의 멤버만 접근 가능>");
System.out.println(product);
System.out.println();
// 2. 자식 타입의 참조 변수로 자식 객체를 다루는 경우
Desktop desktop = new Desktop();
System.out.println();
// desktop 참조 변수로 Product(부모), Desktop(자식)의 멤버에 접근 가능
System.out.println("<desktop 참조 변수로 Product(부모), Desktop(자식)의 멤버에 접근 가능>");
System.out.println(desktop);
System.out.println(desktop.isAllInone()); // Desktop extends Product (상속)
System.out.println();
// 3. 부모 타입의 참조 변수로 자식 객체를 다루는 경우(다형성 적용)
System.out.println("3. 부모 타입의 참조 변수로 자식 객체를 다루는 경우(다형성 적용)");
product = /* (Product) */ new Desktop(); // 자동 형 변환 // product는 위에서 변수 선언했으므로 그대로 사용.
// product 참조 변수로 Product의 멤버만 접근 가능
// 하지만 Desktop의 멤버에 접근하고 싶을 때는 형 변환을 해야 한다.
System.out.println(product); // 부모 타입의 참조 변수이기 때문에 자식 객체의 메서드는 사용할 수 없고 부모 타입의 메서드만 사용할 수 있다.
System.out.println(product.toString());
//└오버라이딩 -> Product, DeskTop 둘다 toStrig이 있지만 Desktop에 있는 toString이 실행됨, 동적 바인딩 때문에 가능.
System.out.println(((Desktop)product).isAllInone()); // 강제 형 변환 -> Desktop에 있는 메서드 사용 가능
System.out.println();
바인딩
- 실제 실행할 메소드 코드와 호출하는 코드를 연결시키는 것을 바인딩이라 한다.
- 프로그램이 실행되기 전에(컴파일 시점) 바인딩이 일어날 경우 정적 바인딩이라 한다.
- 정적 바인딩은 오버라이딩이 불가능한 메소드에서 발생한다. (static, private, final 메소드)
- 프로그램이 실행되면서 객체 타입을 기준으로 바인딩 되는 것을 동적 바인딩이라 한다.
- 동적 바인딩은 다형성을 가능하게 만들어주는 핵심적인 개념으로 오버라이딩된 메소드에서 발생한다.
instaceof연산자
- 참조변수의 형변환 가능여부 확인에 사용하며 가능하면 true 반환.
- 참조변수 instanceof 타입(클래스명)
System.out.println("<instanceof 연산자>");
for(Product p : arr3) {
/*
* instanceof 연산자
* - 참조 변수가 실제로 어떤 클래스 타입의 객체의 주소를 참조하는지 확인할 때 사용.
*/
// System.out.println(((Desktop)p).isAllInone()); // error
if(p instanceof Desktop) {
System.out.println(((Desktop)p).isAllInone());
} else {
System.out.println(((SmartPhone)p).getMobileAgency());
}
// 오버라이딩의 개념을 확용해서 실제로 참조하고 있는 객체의 메소드를 찾아서 실행한다.
// System.out.println(p.toString());
System.out.println();
}
참조변수와 인스턴스의 연결
- 메서드 → 조상 클래스의 메서드를 자손 클래스에서 오버라이딩한 경우 참조 변수의 타입에 상관없이 오버라이딩 된 메서드가 호출되지만 멤버변수의 경우 참조변수의 타입에 따라 달라진다.
- static메서드는 static변수처럼 참조변수의 타입에 영향을 받는다. → 영향을 받지 않는 건 인스턴스메서드뿐이다.
- static메서드는 참조변수가 아닌 클래스이름.메서드()로 호출해야 한다.
- 자손 클래스에서 조상 클래스의 멤버를 중복으로 정의하지 않았을 때는 참조변수의 타입에 따른 변화는 없다.
매개변수의 다형성
- 매개 변수를 조상 타입의 참조변수로 설정하면 메서드의 매개변수로 조상 클래스의 자손타입의 참조변수 어느 것이나 매개 변수로 받아들일 수 있다.
- 위 예제는 고객(Buyer)이 buy(Product p)메서드를 이용해서 Tv와 Computer를 구입하고, 고객의 잔고와 보너스 점수를 출력하는 예제이다.
- 매개변수가 Product 타입의 참조변수라는 것은 메서드의 매개변수로 Product클래스의 자손 타입의 참조변수면 어느 것이나 매개변수로 받아들일 수 있다는 뜻이다.
- Product클래스의 price와 bonusPoint가 선언되어 있기 때문에 참조변수 p로 인스턴스의 price와 bonusPoint를 사용할 수 있다.
// 매개 변수의 다형성
print(new Desktop());
print(new SmartPhone());
}
// 비효율적
// public static void print(Desktop desktop) { // static 메소드니까 호출하려면 static이여야 한다.-> 인스턴스 접근 불가
// System.out.println(desktop);
// }
//
// public static void print(SmartPhone smartPhone) {
// System.out.println(smartPhone);
// }
public static void print(Product product) { // 하나의 메서드로 두 개의 타입을 받아서 처리 가능.
System.out.println("<매개 변수의 다형성>");
System.out.println(product);
}
여러 종류의 객체를 배열로 다루기
- 조상 타입의 참조변수 배열을 사용하면, 공통의 조상을 가진 서로 다른 종류의 객체를 배열로 묶어서 다룰 수 있다.
// 다형성을 사용하기 전에는 Desktop, SmartPhone 배열을 만들어야 한다.
Desktop[] arr1 = new Desktop[2];
arr1[0] = new Desktop();
arr1[1] = new Desktop();
SmartPhone[] arr2 = new SmartPhone[2];
arr2[0] = new SmartPhone();
arr2[1] = new SmartPhone();
// 다형성을 적용하면 부모 클래스의 참조 변수로 자식 객체들을 가리킬 수 있다.
Product[] arr3 = new Product[4];
arr3[0] = new Desktop("a1111", "아이맥 24인치", "애플", 2000000, true);
arr3[1] = new Desktop("d-01", "매직스테이션", "삼성", 1500000, false);
arr3[2] = new SmartPhone("a2222", "아이폰 12 미니", "애플", 960000, "KT");
arr3[3] = new SmartPhone("s-01", "갤럭시 22", "삼성",600000, "SKT" );
'Java' 카테고리의 다른 글
객체(Object) - 인터페이스(interface) (0) | 2022.09.25 |
---|---|
객체(Object) - 추상클래스(abstract class) (0) | 2022.09.24 |
객체(Object) - 제어자(modifier) (0) | 2022.09.22 |
객체(Object) - package & import (0) | 2022.09.21 |
객체(Object) - 오버라이딩(overriding) (0) | 2022.09.20 |