다중정의는 신중히 사용하라

재정의된 메서드와 다중정의 메서드

다중정의된 메서드

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<?> 타입이다. 런타임에는 타입이 매번 달라지지만, 호출할 메서드를 선택하는 데는 영향을 주지 못 한다.
다중정의된 메서드 사이에서는 객체의 런타임 타입은 중요하지 않다. 선택은 컴파일 타임에 오직 매개변수의 컴파일타임 타입에 의해 이뤄진다.

재정의된 메서드

class Wine {
    String name() { return "포도주"; }
}

class SparklingWine extends Wine {
    @Override String name() { return "발포성 포도주"; }
}

class Champagne extends SparklingWine {
    @Override String name() { return "샴페인"; }
}

public class Overriding {
    public static void main(String[] args) {
        List<Wine> wineList = List.of(
                new Wine(), new SparklingWine(), new Champagne());

        for (Wine wine : wineList)
            System.out.println(wine.name());
    }
}

위 프로그램은 “포도주”, “발포성 포도주”, “샴페인”을 차례로 출력한다. for 문에서의 컴파일타임 타입이 모두 Wine인 것에 무관하게 항상 가장 하위에서 정의한 재정의 메서드가 실행되는 것이다.

재정의한 메서드는 런타임에 동적으로 선택되고, 다중정의한 메서드는 컴파일타임에 정적으로 선택된다.
매개변수 수가 같은 다중정의는 만들지 말자. 가변인수를 사용하는 메서드라면 다중정의를 아예 하지 말아야한다. 다중정의 대신 메서드를 이름을 다르게 지어주는 것이 좋다.

메서드 다중정의와 함수형 인터페이스

new Thread(System.out::println).start();

ExecutorService exec = Executors.newCachedThreadPool();
exec.submit(System.out::println); // 컴파일오류 발생

넘겨진 인수는 System.out::println으로 똑같고, 양쪽 모두 Runnable을 받는 형제 메서드를 다중정의하고 있다. 문제는 submit 다중정의 메서드 중에는 Callable<T>를 받는 메서드가 있다는 데 있다. 참조된 메서드(println)와 호출한 메서드(submit) 양쪽 다 다중정의되어, 다중정의 해소 알고리즘이 기대한 것처럼 동작하지 않는다.

다중정의된 메서드들이 함수형 인터페이스를 인수로 받을 때, 비록 서로 다른 함수형 인터페이스라도 인수 위치가 같으면 혼란이 생긴다. 따라서 메서드를 다중정의할 때, 서로 다른 함수형 인터페이스라도 같은 위치의 인수로 받아서는 안 된다.

일반적으로 매개변수 수가 같을 때는 다중정의를 피하는 게 좋다. 상황에 따라, 특히 생성자라면 이 조언을 따르기가 불가능할 수 있다. 그럴 때는 헷갈릴 만한 매개변수는 형변환하여 정확한 다중정의 메서드가 선택되도록 해야 한다. 이것이 불가능하면, 예컨대 기존 클래스를 수정해 새로운 인터페이스를 구현해야 할 때는 같은 객체를 입력받는 다중정의 메서드들이 모두 동일하게 동작하도록 만들어야 한다. 그렇지 못하면 프로그래머들은 다중정의된 메서드나 생성자를 효과적으로 사용하지 못할 것이고, 의도대도 동작하지 않는 이유를 이해하지도 못할 것이다.

Comments