다중정의는 신중히 사용하라
Overloading은 정의된 것 중, 어느 메서드를 호출할지는 컴파일타임에 정해진다. 즉, 런타임에는 타입이 매번 달라지지만 호출할 메서드를 선택하는 데는 영향을 주지 못한다는 것이다.
public class CollectionClassifier {
public static String classify(Set<?> s) {
return "Set";
}
public static String classify(List<?> lst) {
return "List";
}
public static String classify(Collection<?> c) {
return "Unknown Collection";
}
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));
}
}
}
위의 예시에서 HashSet, ArrayList, HashMap을 인자로 넘기고 있어 Set, List, Unknown Collection이 호출될 것 같지만 실제로는 Unknown Collection만 호출된다.
Overloading한 메서드는 정적으로 선택되는 반면, Override한 메서드는 동적으로 선택된다. 즉 하위에서 재정의하였다면 재정의된 메서드가 실행된다.
매개변수 수가 같은 다중정의는 만들지 않는 것이 좋다. 차라리 메서드 이름을 다르게 짓는게 낫다. 생성자의 경우, 다중정의를 할 수 밖에 없다. 하지만 정적 팩터리를 사용해 의미있는 메서드로 정의할 수 있다.
만약 어떤 다중정의 메서드가 불리는지 몰라도 기능이 똑같다면 신경쓸 건 없다.
여러 생성자가 같은 수의 매개변수를 받아야 하는 경우, 매개변수 중 하나 이상이 근본적으로 다르다면 (서로 형변환 불가) 헷갈릴 일이 없다. 이 조건만 충족하면 어느 다중정의 메서드를 호출할지가 매개변수들의 런타임 타입만으로 결정된다. 따라서 컴파일타임 타입에는 영향받지 않을 수 있게 된다.
public static void main(String[] args) {
Set<Integer> set = new TreeSet<>();
List<Integer> list = new ArrayList<>();
for (int i = -3; i < 3; i++) {
set.add(i);
list.add(i);
}
for (int i = 0; i < 3; i++) {
set.remove(i);
list.remove(i);
}
System.out.println(set + " " + list);
}
위 코드에서 remove를 할 때, Set은 원소 값을 지우고 list는 인덱스에 해당하는 값을 지운다. list의 remove(Integer i)가 실제 원소 값을 지우는 것으로, Integer로 형변환한다면 remove(Object)를 호출해 원하는대로 0, 1, 2를 지울 수 있다.
(근본적으로 형변환이 가능해서 혼돈을 야기했다는 예시같다..)