public class CollectionClassifier {
public static String classify(Set<?> s) {
return "집합";
}
public static String classify(List<?> lst) {
return "리스트";
}
public static String classify(Collection<?> c) {
return "그 외";
}
public static void main(String[] args) {
Collection<?>[] collections = new Collection<?>[] {
new HashSet<String>(),
new ArrayList<BigInteger>(),
new HashMap<String, String>().values()
};
for (Collection<?> c : collections) {
System.out.println(classify(c));
}
}
}
- "집합", "리스트", "그 외"를 차례로 출력할 것 같지만, 실제로 수행해보면 "그 외"만 세 번 연달아 출력한다.
- 직관이 어긋나는 이유는 재정의한 메서드는 동적으로, 다중정의한 메서드는 정적으로 선택되기 때문이다.
- 다중정의된 메서드 선택은 컴파일타임에, 오직 매개변수의 컴파일타임 타입에 의해 이뤄진다.
어떻게 사용했을 때 다중정의가 혼란을 줄까?
- 매개변수 수가 같을 때
- 가변인수를 사용할 때
- 다중정의하는 대신 메서드 이름을 다르게 지어주는 길도 있다.
- 생성자는 이름을 다르게 지을 수 없으니 두 번째부터는 무조건 다중정의된다. 하지만 정적 팩터리라는 대안을 활용할 수 있는 경우가 많다.
- 매개변수 중 하나 이상이 "근본적으로 다르다"면 헷갈릴 일이 없다.
- 근본적으로 다르다는 건 두 타입의 (null이 아닌) 값을 서로 어느 쪽으로든 형변환할 수 없다는 뜻이다.
- 오토박싱, 제네릭, 람다와 메서드 참조가 다중정의 시의 혼란을 키웠다.
- 메서드를 다중정의할 때, 서로 다른 함수형 인터페이스라도 같은 위치의 인수로 받아서는 안 된다.
- 다중정의된 메서드 중 하나를 선택하는 규칙은 매우 복잡하며, 자바가 버전업될수록 더 복잡해지고 있어, 이 모두를 이해하고 사용하는 프로그래머는 극히 드물 것이다. 어떤 다중정의 메서드가 불리는지 몰라도 기능이 똑같다면 신경 쓸 게 없다.
- 이렇게 하는 가장 일반적인 방법은 상대적으로 더 특수한 다중정의 메서드에서 덜 특수한 다중정의 메서드로 일을 넘겨버리는 것이다.