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 HashSet<String>(),
new ArrayList<BigInteger>(),
new HashMap<String, String>().values()
};
for (Collection<?> c : collections) {
System.out.println(classify(c));
}
}
}
위의 코드의 결과는 "집합", "리스트", "그 외"일 것이라 생각하지만 실제로는 "그 외"만 세 번 연달아 출련된다.
그 이유는 classify에 중 어느 메서드를 호출할지가 컴파일타임에 정해지기 때문이다!!!!
컴파일타임에는 for 문 안의 c는 항상 Collection<?> 타입이다.
런타임에는 타입이 매번 달라지지만, 호출할 메서드를 선택하는 데 영향을 주지 못한다.
따라서 컴파일 타임의 매개변수 타입을 기준으로 항상 세 번째 메서드가 호출되는 것이다.
이는 재정의한 메서드(override)는 동적으로 선택되는 반면, 다중정의한 메서드는 정적으로 선택되기 때문이다.
메서드를 재정의했다면 해당 객체의 런타임 타입이 어떤 메서드를 호출할지의 기준이 된다.
하지만 다중정의된 메서드 사에서는 객체의 런타임 타입은 전혀 중요하지 않다.
선택은 컴파일타임에, 오직 매개변수의 컴파일 타입에 의해 이뤄진다.
그렇기에 다중정의가 혼동을 일으키는 상황을 피해야 한다.
안전하고 보수적으로 가려면 매개변수 수가 같은 다중정의는 만들지 말자.
가변인수를 사용하는 메서드라면 다중정의를 아예 하지 말아야 한다.
다중정의를 하는 대신 메서드 이름을 다르게 지어주는 길도 항상 열려 있다는 것을 생각하자.
하지만 매개변수 수가 같은 다중정의 메서드가 많더라도, 그중 어느 것이 주어진 매개변수 집합을 처리할지가 명확히 구분된다면 헷갈릴 일은 없을 것이다.
즉, 매개변수 중 하나 이상이 "근본적으로 다르다"면 헷갈릴 일이 없다.
근본적으로 다르다는 건 두 타입의 값을 서로 어느쪽으로든 형변환할 수 없다는 뜻이다.
이 조건만 충족하면 어느 다중정의 메서드를 호출할지가 매개변수들의 런타임 타입만으로 결정된다.
따라서 컴파일타임 타입에는 영향을 받지 않게 되고, 혼란을 주는 주된 원인이 사라진다.
그리고 메서드를 다중정의할 때, 서로 다른 함수형 인터페이스라도 같은 위치의 인수로 받아서는 안 된다.
이는 서로 다른 함수형 인터페이스라도 서로 근본적으로 다르지 않다는 뜻이다.