왜 조상 클래스에서 자손 클래스 메서드를 호출하나
학습 배경
이펙티브자바 아이템 18. 상속보다는 컴포지션을 사용하라에서 유명한 예시가 나온다.
public class InstrumentedHashSet<E> extends HashSet<E> {
// 추가된 원소의 수
private int addCount = 0;
public InstrumentedHashSet() {
}
@Override
public boolean add(E e) {
addCount++;
return super.add(e);
}
@Override
public boolean addAll(Collection<? extends E> c) {
addCount += c.size();
return super.addAll(c);
}
public int getAddCount() {
return addCount;
}
}
InstrumentedHashSet<String> languages = new InstrumentedHashSet<>();
languages.addAll(Arrays.asList("Java", "Ruby", "Scala"));
HashSet
public class HashSet<E>
extends AbstractSet<E> ... {
...
}
AbstractSet
public abstract class AbstractSet<E> extends AbstractCollection<E> ... {
...
}
AbstractCollection
public abstract class AbstractCollection<E> implements Collection<E> {
...
public boolean addAll(Collection<? extends E> c) {
boolean modified = false;
for (E e : c)
if (add(e))
modified = true;
return modified;
}
}
위 코드를 실행하면 languages에서 getAddCount
를 했을 때 일반적으로는 3이 나올 것을 기대한다. 하지만 실제로는 6이 나온다. 그 이유는 HashSet
의 addAll
에서 반복문을 통해 아이템 개수만큼 또 add
를 호출하기 때문이다.
여기서 의문이 생겼다. 부모클래스에 메서드가 정의되어 있으면 부모 클래스의 메서드를 써야지 왜 자손클래스의 메서드를 호출하는가?
학습
메서드 탐색
객체지향 시스템에서는 다형성 덕분에 동적으로 어떤 메서드가 실행될지 결정된다. 이때 실행할 메서드를 선택하는데 규칙이 있다.
- 우선 메시지를 수신한 객체가 자신을 생성한 클래스에 적합한 메서드가 있는지 검사한다. 있으면 실행
- 메서드가 없으면 부모 클래스에서 메서드를 탐색한다.
- 이걸 최상위 클래스까지 반복하고 메서드를 못찾으면 예외가 발생한다. (자바에서는 컴파일에러)
self (this)
위의 과정을 수행하기 전에, 객체가 메시지를 수신하면 컴파일러는 self 참조라는 임시 변수를 자동으로 생성한 후 메시지를 수신한 객체를 가리키도록 동작한다.
정적 타입 언어에 속하는 자바, C++ 에서는 this, 동적타입언어에서는 주로 self를 사용한다.
코드에서 메시지를 수신한 객체는 languages
다. self 참조는 InstrumentedHashSet
의 인스턴스를 가리키도록 설정된다. languages
에 addAll()
메시지를 보낸다. languages
는 그럼 자신을 생성한 클래스인 InstrumentedHashSet
에서 addAll()
을 실행하는데 코드를 보면 super.addAll()
이 있다. 그래서 조상클래스에서 탐색하기 위해 바로 위인 HashSet
으로 간다.
HashSet
에 addAll
을 처리할 수 있는 적절한 메서드가 없기 때문에 조상클래스들을 따라가다가 AbstractCollection에서 addAll()
을 찾아낸다. 하지만 여기서 add()
를 전송하는 구문과 마주치게 된다. 이제 메서드 탐색은 self 참조가 가리키는 객체에서 시작된다. 따라서 이때 InstrumentedHashSet
의 add()
가 존재하므로 InstrumentedHashSet
의 메서드가 사용된다.
결과
처음에는 왜 부모 클래스에서 부모클래스에 정의되어있는 메서드를 사용하지 않고 자손 클래스의 메서드를 쓰는지 이해하지 못했다. 그냥 언어에서 그렇게 정했나보다라고 막연히 생각했다. 하지만 그렇게 규칙을 정하는데는 분명히 원리가 있었을텐데 그 원리를 알 수 있게 되었다.
참조
오브젝트 12장 - 다형성
Author And Source
이 문제에 관하여(왜 조상 클래스에서 자손 클래스 메서드를 호출하나), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@injoon2019/왜-조상-클래스에서-자손-클래스-메서드를-호출하나저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)