Effective Java 2/E, 오버로딩할 때는 주의하라

|

아래 프로그램의 결과를 예상해 보자. 해당하는 리스트의 타입 별로 세번 출력할까?

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));
        }
    }
}

예상과는 달리 Unknown Collection 을 세번 출력한다. 왜 그럴까?
그 이유는 오버로딩된 메서드 가운데 어떤 것이 호출될지는 컴파일 시점에 결정되기 때문이다.

오버로딩된 메서드는 정적으로 선택되지만, 재정의(Overriding) 된 매서드는 동적으로 선택된다.

class Wine {
    String name() { return "wine"; }
}

class SparklingWine extends Wine {
    String name() { return "sparkling wine"; }
}

class Champagne extends SparklingWine {
    String name() { return "champagne"; }
}

public class Overriding {
    public static void main(String[] args) {
        Wine[] wines = {
          new Wine(), new SparklingWine(), new Champagne()
        };
        for(Wine wine : wines) {
            System.out.println(wine.name());
        }
    }
}

위 코드는 오버라이딩에 대한 예제이다. 위의 코드의 컴파일 시점의 자료형은 샴페인, 스파클링 모두 Wine이었지만 컴파일 시점 자료형이 오버라이딩에 아무런 영향을 주지 못했다.

그럼 객체 타입에 따라 다른 함수를 부르고 싶을때 어떻게 구현할까?

public static String classify(Collection<?> c) {
    return c instanceof Set ? "Set" : c instanceof List? "List" : "Unknown Collection";
        }

이렇게 instanceof를 사용하여 자료형을 검사하자.

오버로딩을 사용할 때는 혼란스럽지 않게 사용할 수 있도록 주의하자!

오버로딩은 혼란스러운 상황을 발생시킬 수 있다.
인자 개수가 같은 오버로딩 메소드를 추가하는 것은 일반적으로 피해야 한다.