티스토리 뷰

Item52에서는 다중 정의를 신중히 사용하라 말한다

책의 나온 예제 코드만 보면 매개변수에 따라 다른 값이 출력될 것 같지만

실제로는 Collection 타입으로 인식되어 세 번 다 "Collection"만 나오게 된다

public class CollectionClassifier {

  public static String classify(Set<?> s) {
    return "Set";
  }

  public static String classify(List<?> s) {
    return "List";
  }

  public static String classify(Collection<?> s) {
    return "Collection";
  }

  public static void main(String[] args) {
    Collection<?>[] collections = {
      new HashSet<String>(),
      new ArrayList<Integer>(),
      new HashMap<String, String>().values()
    };

    // Collection 만 세번 나온다
    for (Collection<?> collection : collections) {
      System.out.println(CollectionClassifier.classify(collection));
    }
}

 

이러한 이유는 재정의한 메서드는 동적으로 선택되고, 다중정의한 메서드는 정적으로 선택되기 때문이란다

동적 선택은 런타임에 타입을 파악하는 것이고 정적 선택은 컴파일 타임에 타입을 파악하는 것이다

그 결과로 Set, List를 인수 자리에 넣어도 Collection으로 인식돼버린 것이다

이를 피하기 위한 방법은 아래와 같다

1. 매개변수 개수가 같은 다중정의를 하지 않거나

2. 타입이 완전히 달라 서로 간에 형 변환이 되지 않도록 하거나

3. 기능 자체가 같아버리면 된다

 

3번의 예로 String의 contentEquals() 메서드가 있는데 StringBuffer, CharBuffer를 인수로 받고

단순히 forwarding만 하는데 이는 StringBuffer를 받는 contentEquals가 jdk 1.4에서 설계되었고

문자 관련 공통 인터페이스인 CharSequence가 jdk 1.5부터 등장했기 때문에 아직 남아있는 것이다

사실 3번은 기능이 같으면 애초에 다중정의할 필요가 없다, 별 의미가 없는 짓거리니 패스하도록 하자

 

 

1번은 다중정의의 목적에 맞지 않는 극단적인 선택이라 생각된다

같은 매개변수 타입을 받아도 처리하는 작업이 다를 때

ObjectOuputStream의 writeBoolean, writeByte... 등

메서드 이름 별로 세세하게 나눠두어 다중 정의인 척하는 방법 또한 존재한다

 

 

다중 정의를 설계할 때의 의도에 맞게 써야 하니 나는 2번이 가장 합리적인 해결책 같다

요렇게 쓰지 않으면 람다 사용 시 타입을 헷갈려할 수 있으니 명시적인 형 변환 코드가 들어가야 하는데 보기 좋은 형태는 아니다

결론적으로 가능하다면 같은 공통 인터페이스를 가지는 녀석들은 같은 기능을 하도록 만들어주고 공통 인터페이스로 받는다

만약 그렇게 할 수 없는 상황이라면 재정의를 이용하여 런타임 시 타입 정보를 파악할 수 있도록 하자

이런 세세한 사항은 언제나 그렇듯 공부할 때만 기억이 선명하니 반드시 테스트 코드를 작성하자

댓글
링크
글 보관함
«   2024/09   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
Total
Today
Yesterday