1. 상속의 참조 관계
상속에 있어서의 참조관계로 다음과 같이 일반적인 상황이 있을 수 있다.
class First {...}
class Second extends First {...}
class Third extends Second {...}
위 코드는 문제없이 컴파일이 된다.
상위 클래스의 참조변수로도 하위 클래스의 인스턴스 참조가 가능하기 때문이다.
전체적으로 Third는 Second를 상속하고 연쇄적으로 Second는 First를 상속한다. 따라서 Third도 First를 당연히 상속한다.
Third ref1 = ... // compile success
Second ref2 = ref1; // Third is a Second
First ref3 = ref2; // Third is a First
여기서 Third ref1이 무엇이 되었든간에 컴파일에 성공했다면, 그 다음행은 정상적으로 실행된다.
왜냐하면 Third는 Second이기도 하며, Third는 First이기도 하므로 참조가 가능하기 때문이다.
어떤 클래스의 참조변수는 그 클래스의 인스턴스, 또는 그것을 상속하는 하위 클래스의 인스턴스를 참조할 수 있다.
First ref1 = new Third(); // 컴파일러가 ref1을 First 타입으로 판단
Second ref2 = ref1; // First is not a Second, compile error!!
Third ref3 = ref1; // Fisrt is not a Third, compile error!!
First ref1 = new Third(); // 컴파일러가 ref1을 First 타입으로 판단
Second ref2 = new First(); // First is not a Second, compile error!!
Third ref3 = new First(); // Fisrt is not a Third, compile error!!
반면에 위 코드는 컴파일 에러가 발생한다.
First타입의 참조변수 ref1에 Third의 인스턴스를 담지만 컴파일러는 ref1을 First타입으로 판단하므로,
나머지 행에서 하위 클래스인 Second와 Third의 참조변수에 상위 클래스인 First타입의 참조값을 담으려고 하자 컴파일 에러가 발생한다.
사실상 위의 두 코드는 같은 이유로 컴파일 에러가 발생하는 것이다.
더 큰 그릇에 작은 그릇을 담을 수는 없는 법이다.
First ref1 = new Third();
Second ref2 = (Third)ref1; // 참조변수 ref1의 형 변환
Third ref3 = (Third)ref1; // 참조변수 ref1의 형 변환
물론 이와같이 참조변수의 형 변환을 통해 정상적인 컴파일이 가능하다.
하지만 이것은 매우 드물게 발생하는 상황이며, 이때 올바른 코드의 구성인가를 다시 살펴볼 필요가 있다.
2. 오버라이딩(Override)과 오버로딩(Overload)
class GrandParents {
public void rideMethod() { System.out.println("GrandParants' rideMethod"); }
public void loadMethod() { System.out.println("GrandParants' loadMethod"); }
}
class Parents extends GrandParents {
public void rideMethod() { System.out.println("Parants' rideMethod"); }
public void loadMethod(int n) { System.out.println("Parants' loadMethod"); }
}
class Child extends Parents {
public void rideMethod() { System.out.println("Child's rideMethod"); }
public void loadMethod(double n) { System.out.println("Child's loadMethod"); }
}
class RideLoad {
public static void main(String[] args) {
GrandParents ref1 = new Child();
Parents ref2 = new Child();
Child ref3 = new Child();
ref1.rideMethod(); // Child.rideMethod() 호출
ref2.rideMethod(); // Child.rideMethod() 호출
ref3.rideMethod(); // Child.rideMethod() 호출
ref1.loadMethod(); // GrandParents.loadMethod() 호출
ref2.loadMethod(1); // Parents.loadMethod(int n) 호출
ref3.loadMethod(1.2); // Child.loadMethod(double n) 호출
}
}
GrandParents타입의 참조변수 ref1은 Child타입의 인스턴스를 참조한다.
ref2와 ref3도 마찬가지로 Child인스턴스를 각각 참조한다.
즉, 참조변수의 타입은 모두 다르지만, 최하위 클래스인 Child의 인스턴스를 참조한다.
Child's rideMethod
Child's rideMethod
Child's rideMethod
GrandParants' loadMethod
Parants' loadMethod
Child's loadMethod
출력결과를 보면 참조변수의 자료형에 관계없이 최하위 클래스인 Child의 rideMethod만 호출되었다는 사실을 알 수 있다.
이에 비해 loadMethod는 각각 호출부분도 다르고 결과도 각각 다르다.
오버라이딩에 비해 오버로딩된 메소드는 사실상 이름만 같을 뿐 서로 다른 메소드라고 볼 수 있다.
- 상위 클래스의 참조변수는 하위 클래스의 인스턴스를 참조할 수 있다.
- 오버라이딩된 상위 클래스의 메소드는 오버라이딩을 한 하위 클래스의 메소드에 의해 가려진다. 즉, 외부에서는 참조변수를 통해서 오버라이딩된 상위 클래스의 메소드를 호출할 수 없다. (내부에서는 super키워드로 호출가능!)
'📌 java > Object-oriented Programming' 카테고리의 다른 글
Homework_W7 (상속 ≠ 오버라이딩) (0) | 2020.05.02 |
---|---|
java - 업캐스팅 다운캐스팅 (0) | 2020.04.26 |
Homework_W6 (0) | 2020.04.23 |
Homework_W5 (0) | 2020.04.14 |
Homework_W4 (0) | 2020.04.07 |