한 타입의 참조변수로 여러 타입의 객체를 참조할 수 있는 것을 의미한다.
조상클래스 타입의 참조변수로 자손클래스의 인스턴스를 참조할 수 있도록 하는 것이다.
(반대로 자손타입의 참조변수로 조상타입의 인스턴스를 참조할 수는 없다.)
자손객체를 생성해서 부모타입의 참조변수에 대입할 수 있다.
- 실제 생성한 자손객체의 주소값이 참조변수에 대입되는 것이 아니라, 실제 생성한 객체와 연결된 조상객체 중에서 참조변수의 타입과 일치하는 조상객체의 주소값이 참조변수에 대입한다.
Object객체의 클래스의 최고 조상객체이므로 참조변수에는 자바의 모든 객체를 저장할 수 있다.
class Tv {
boolean power;
int channel;
void power() { power = !power; }
void channelUp() { ++channel; }
void channelDown() { --channel; }
}
class SmartTv extends Tv {
String text;
void caption() { ... }
}
일반적인 객체 생성
SmartTv s = new SmartTv();
- SmartTv 인스턴스의 모든 멤버를 사용할 수 있다.
다형성을 이용한 객체 생성
Tv t = new SmartTv();
- Tv클래스의 멤버들(상속받은 멤버포함)만 사용할 수 있다.
SmartTv s = new Tv();
- 오류
- 참조변수의 타입과 생성한 객체의 타입이 서로 다르다
- super를 통해 생성한 객체의 조상타입 객체 중에서 참조변수의 타입과 같은 객체가 있는지 탐색한다
- 조상객체 중에서 참조변수의 타입과 같은 객체가 발견되면 해당 조상객체의 주소값을 참조변수에 대입한다.
(참조변수는 해당 조상객체를 바라보고 있다.)
1. 하나의 타입으로 다양한 객체의 타입을 참조할 수 있다.
SmartPhone p;
- p참조변수는 SmartPhone Galaxy, Iphone객체를 참조할 수 있다.
- 하나의 타입으로 여러 객체를 관리(저장)할 수 있다.
Phone[] p = new Phone[10];
- p가 참조하는 배열은 Phone 타입객체 10개를 저장할 수 있다.
- p가 참조하는 배열의 각 요소에는 Phone, SmartPhone, Iphone, FeaturePhone, Galaxy 객체를 저장할 수 있다.
- p가 참조하는 배열의 각 요소에는 통화기능, 문자기능이 있는 모든 Phone종류를 저장할 수 있다.
SmartPhone[] p = new SmartPhone[10];
- p가 참조하는 배열은 SmartPhone타입객체 10개를 저장할 수 있다.
- p가 참조하는 배열의 각 요소에는 SmartPhone, Iphone, Galaxy 요소를 저장할 수 있다.
- p가 참조하는 배열의 각 요소에는 통화기능 문자기능, 게임기능, 인터넷 기능이 있는 모든 SmartPhone종류를 저장할 수 있다.
2. 참조변수의 타입으로 객체의 종류를 제한할 수 있다.
public void testInternet(Phone p) { ... }
- 통화 기능을 테스트하는 메소드
- 매개변수의 타입을 Phone타입으로 지정함으로써 call() 기능이 제공되는 모든 Phone류 객체를 전달받을 수 있다.
public void testInternet(SmartPhone p) { ... }
- 인터넷 기능을 테스트하는 메소드
- 매개변수의 타입을 SmartPhone타입으로 지정함으로써 internet() 기능이 제공되는 모든 SmartPhone류 객체를 전달받을 수 있다.
public void testApplePay(Ipone p) { ... }
- 애플페이 기능을 테스트하는 메소드
- 매개변수의 타입을 Iphone타입으로 지정함으로써 applePay() 기능이 제공되는 모든 Iphone류 객체를 전달받을 수 있다.
3. 참조변수의 타입으로 반환타입의 종류를 제한할 수 있다.
public Phone createPhone() { ... }
- 이 메소드는 모든 Phone류 객체를 반환할 수 있다.
public SmartPhone createSmartPhone() { ... }
- 이 메소드는 모든 SmartPhone류 객체를 반환할 수 있다.
기본형 변수처럼 참조변수도 형변환이 가능하다. 단, 서로 상속관계에 있는 클래스 사이에서만 가능하다.
기본형의 형변환과 달리 참조형의 형변환는 변수에 저장된 값이 변환하는 것이 아니다.
바로 윗 조상이나 자손이 아닌, 조상의 조상으로도 형변환이 가능하다.
참조변수의 형변환은 그저 리모컨(참조변수)를 다른 종류의 것으로 바꾸는 것 뿐이다. 리모컨의 타입을 바꾸는 이유는 사용할 수 있는 멤버 개수를 조절하기 위한 것이다.
class Car { }
class FireEngine extends Car { }
class Ambulan
FireEngine f = new FireEngine();
Car c = (Car)f;
- 조상인 Car타입으로 형변환이 가능하다.
- f의 값(객체의 주소)을 c에 저장한다.
- 자손타입을 조상타입으로 형변환하는 경우이기 때문에 생략이 가능하다.
- 다룰 수 있는 멤버의 개수가 줄어들기 때문에 항상 안전하다. 그러므로 형변환을 생략할 수 있다.
FireEngine f2 = (FireEngine)c;
// 자손인 FireEngine타입으로 형변환 가능
Ambulance a = (Ambulance)f
// 상속관계가 아닌 클래스 간의 형변환 불가능
Phone p1 = new FeaturePhone();
p1.call(); // Phone
p1.sms(); // Phone
p1.toString(); // Object
p1.mp3(); // 실행할 수 없다.
- (FeaturePhone)p1은 p1참조변수가 참조하는 객체를 기준으로 FeaturePhone객체를
탐색해서 해당객체가 발견되면 그 객체의 주소값을 반환한다.
단, 생성된 객체에 포함된 객체만 가능하다.
FeaturePhone x = (FeaturePhone)p1;
x.mp3(); // 실행할 수 있다.
SmartPhone y = (SmartPhone)p1; // 형변환 오류
Phone p3 = new Iphone();
p3.toString();
p3.call();
p3.sms();
p3.internet(); // 실행할 수 없다.
p3.game(); // 실행할 수 없다.
p3.facetime(); // 실행할 수 없다.
p3.applePay(); // 실행할 수 없다.
SmartPhone y = (SmartPhone)p3;
y.internet(); // 실행할 수 있다.
y.game(); // 실행할 수 있다.
Iphone z = (Iphone)p3;
z.facetime(); // 실행할 수 있다.
z.applePay(); // 실행할 수 있다.
형식)
bolean result = 참조변수 instanceof 클래스 이름
class Point {
int x;
int y;
String getLocation() {
return "x :" + x + ", y :" + y;
}
}
class Point3D extends Point {
int z;
// 메서드 재정의
String getLocation() {
return "x :" + x + ", y :" + y + ", z :" + z;
}
}
메소드 재정의 관계가 있는 메소드는 클래스 형변환 되어서 실제 생성한 객체를 참조하지 않고도 재정의된 메소드를 실행할 수 있다.
실제 생성한 객체에 재정의 메소드를 실행하기 위해서 실제 생성한 객체를 참조하도록 강제형변환할 필요가 없다.
메서드의 선언부(반환타입, 메서드 이름, 매개변수)는 조상의 것과 완전히 일치해야 한다.
접근 제어자는 조상 클래스의 메서드와 일치하거나 더 넓어야한다.
- 조상 클래스에 정의된 메서드의 접근 제어자가 protected라면, 자손 클래스에 정의된 메서드의 접근 제어자는 protected나 public이어야 한다.
조상 클래스의 메서드보다 많은 수의 예외를 선언할 수 없다.
오버로딩 vs 오버라이딩
- 오버로딩
- 기존에 없는 새로운 메서드를 정의하는 것- 오버라이딩
- 상속받은 메서드의 내용을 변경하는 것
class Parent { void parentMethod() {수행문1;} } class Child extends Parent { void parentMethod() {수행문2;} // 오버라이딩 void parentMethod(int x) {수행문1;} // 오버로딩 }