Adventure Time - Lady Rainicorn [아이템 52] 다중정의는 신중히 사용하라
본문 바로가기
🤓 스터디/이펙티브 자바

[아이템 52] 다중정의는 신중히 사용하라

by 강켄트 2023. 4. 18.

 

다중 정의가 무엇이냐

자바 기초를 공부하신 분이면 다들 알고 계시는 오버로딩(overloading)입니당

"같은 이름의 메소드를 중복하여 정의하는 것을 의미"

 

자바는 한 클래스 내에 같은 이름의 메소드가 둘 이상있어도 

매개변수의 개수나 타입을 다르게 하면, 하나의 이름으로 메소드를 작성이 가능하다.

즉, 앞에서 배운 서로 다른 시그니처를 갖는 여러 메소드를 같은 이름으로 정의하는 것이라고 할 수 있다.

 

그럼 왜 다중정의는 신중해야할까

다중 정의 메서드는 개발자가 기대한 것처럼 동작하지 않는 경우가 있기 때문!

 

52-1) 컬렉션 분류기 

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(CollectionClassifier.classify(c));
        }
    }    
}

//그 외
//그 외
//그 외

 

결과적으로 '그 외'만 세번 출력됐는데,

다중정의된 세 classify 중 어느 메서드를 호출할지가 컴파일타임에 정해지기 때문이다.

컴파일타임에는 for문 안의 변수 c는 Collection<?> 타입이다.

 

"런타임에는 타입이 매번 달라지지만, 호출할 메서드를 선택하는 데는 영향을 주지 못한다.

따라서 컴파일타임의 매개변수 타입을 기준으로 항상 세 번째 메서드인 classify(Collection<?>)만 호출하는 것이다."

 

위의 예시처럼 직관에 어긋나는 이유는

재정의한 메서드는 동적으로 선택되고, 다중정의(overloading)한 메서드는 정적으로 선택되기 때문이다.

 

메서드를 재정의(override) 했다면 해당 객체의 런타임 타입이 어떤 메서드를 호출할지의 기준이 된다.

 

52-2)재정의된 메서드 호출 메커니즘

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인 것에 무관하게 항상 '가장 하위에서 정의한 ' 재정의 메서드가 실행된 것이다.

 

"다중정의된 메서드 사이에서는 객체의 런타임 타입은 전혀 중요치 않다.

선택은 컴파일타임에, 오직 매개변수의 컴파일타임 타입에 의해 이뤄진다."

 

52-1의 CollectionClassifier 클래스도 52-2 Wine 클래스처럼 

런타임 타입에 기초해 적절한 다중정의 메서드로 자동 분배하고 싶으면 다음과 같이 바꿀 수 있다.

public static String classify(Collection<?> c){
   return c instanceof Set ? "집합" : c instanceof List ? "리스트" : "그 외";
}

 

즉, 하고 싶은 말은

52-1 CollectionClassifier 클래스처럼 프로그래머가 기대한 대로(집합, 리스트, 그외가 차례로 출력되는...)

동작하지 않을 수 있으니 다중정의로 혼동을 일으키는 상황을 피하자라는 것이다.

더불어 가변인수를 사용하는 메서드는 다중정의는 더더욱 피해야한다.

 

 

 

 

댓글