코딩하는 털보

이펙티브 자바, 아이템 14. Comparable을 구현할지 고려하라 본문

Book/이펙티브 자바

이펙티브 자바, 아이템 14. Comparable을 구현할지 고려하라

이정인 2021. 8. 28. 22:09

이펙티브 자바, 아이템 14. Comparable을 구현할지 고려하라

Comparable 인터페이스의 compareTo를 구현하여 사용하는 것을 고려해 볼 수 있다.
compareTo 메서드는 Object의 equals와 두 가지 차이가 있다.

  1. 단순 동치성 비교에 더해 순서까지 비교할 수 있다.
    Comparable을 구현했다는 것은 인스턴스들에 자연적인 순서가 있음을 뜻한다.
    알파벳, 숫자, 연대 처럼 순서가 명확한 값 클래스를 작성한다면 반드시 Comparable 인터페이스를 구현하자.
    compareTo 규약을 지키지 못하면 비교를 활용하는 클래스와 어울리지 못한다. (ex. TreeSet, TreeMap 또는 Collections와 Arrays의 정렬)
    compareTo 메서드로 수행하는 동치성 검사도 equals 규약과 같이 반사성, 대칭성, 추이성을 충족해야 한다.
  2. 제네릭
    equals 는 인수의 타입을 확인하거나 형변환하는 작업이 필요했지만 Comparable이 제네릭 인터페이스이므로 compareTo에서는 그런 작업이 필요하지 않다.

객체 참조 필드를 비교할때는 compareTo 메서드를 재귀적으로 호출한다. Comparable 인터에이스를 구현하지 않은 필드나 표준이 아닌 순서로 비교하려면 비교자(Comparator)를 대신 사용한다.

참고사항

  • compareTo 메서드에서 관계 연산자 '<'와 '>'를 사용하는 이전 방식은 거추장스럽고 오류를 유발하니, 추천하지 않는다. 자바 7부터 박싱된 기본 타입 클래스들에 compare 정적 메서드가 추가되었다.

  • 핵심 필드의 비교 순서는 가장 핵심적인 것 부터.

  • 자바 8부터 Comparator 인터페이스가 일련의 비교자 생성 메서드와 팀을 꾸려 메서드 연쇄 방식으로 비교자를 생성할 수 있다. 코드는 깔끔하지만 성능적으로는 좋지 않다고 한다.

        private static final Comparator<PhoneNumber> COMPARATOR =
                Comparator.comparingInt((PhoneNumber pn) -> pn.ariaCode)
                .thenComparingInt((PhoneNumber pn) -> pn.prefix)
                .thenComparingInt((PhoneNumber pn) -> pn.lineNum);
    
        @Override
        public int compareTo(PhoneNumber pn) {
            return COMPARATOR.compare(this, pn);
        }

    Comparator는 comparingInt, thenComparingInt 등 숫자용 기본 타입을 커버하는 보조 생성 메서드를 가지고 있다.
    그리고 comparing 정적 메서드와 thenComparing 메서드는 객체 참조용 비교자 생성 메서드이다.

  • 해시코드 값의 차(o1.hashCode() - o2.hashCode())를 기준으로 비교하면 정수 오버플로를 일으키거나 부동소수점 계산 방식에 따른 오류를 낼 수 있다.
    대신 Integer.compare(o1.hashCode(), o2.hashCode()) 또는 Comparator가 제공하는 비교자 생성 메서드를 활용 Comparator.comparingInt(o -> o.hashCode())

Comments