기록이 힘이다.

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

JAVA

[이펙티브 자바] 14. Comparable을 구현할지 고려하라

dev22 2023. 3. 20. 16:54
728x90

Comparable 인터페이스의 유일무이한 메서드인 compareTo를 알아보자. 성격은 두 가지만 빼면 Object의 equals와 같다. 

compareTo는 단순 동치성 비교에 더해 순서까지 비교할 수 있으며, 제네릭하다. Comparable을 구현했다는 것은 그 클래스의 인스턴스들에는 자연적인 순서가 있음을 뜻한다.

Arrays.sort(a);

검색, 극단값 계산, 자동 정렬되는 컬렉션 관리도 역시 쉽게 할 수 있다. 

public class WordList{
	public static void main(String[] args){
    	Set<String> s = new TreeSet<>();
        Collections.addAll(s, args);
        System.out.println(s);
    }
}

명령줄 인수들을 (중복은 제거하고) 알파벳순으로 출력한다. String이 Comparable을 구현한 덕분이다. 

 

사실상 자바 플랫폼 라이브러리의 모든 값 클래스와 열거 타입이 Comparable을 구현했다. 알파벳, 숫자, 연대 같이 순서가 명확한 값 클래스를 작성한다면 반드시 Comparable 인터페이스를 구현!

 

public interface Comparable<T>{
	int compareTo(T t);
}

compareTo 메서드의 일반 규약은 equals의 규약과 비슷하다. 

모든 객체에 대해 전역 동치관계를 부여하는 equals 메서드와 달리, compareTo는 타입이 다른 객체를 신경 쓰지 않아도 된다. 타입이 다른 객체가 주어지면 간단히 ClassCastException을 던져도 되며, 대부분 그렇게 한다. 물론, 이 규약에서는 다른 타입 사이의 비교도 허용하는데  보통은 비교할 객체들이 구현한 공통 인터페이스를 매개로 이뤄진다. 

 

비교를 활용하는 클래스의 예로는 정렬된 컬렉션인 TreeSet과 TreeMap, 검색과 정렬 알고리즘을 활용하는 유틸리티 클래스인 Collections와 Arrays가 있다. 

 

마지막 규약은 필수는 아니지만 꼭 지키길 바란다 compareTo 메서드로 수행한 동치성 테스트의 결과가 equals와 같아야 한다는 것이다. 이를 잘 지키면 compareTo로 줄지은 순서와 equals의 결과가 일관되게 된다. 

 

Compareable은 타입을 인수로 받는 제네릭 인터페이스이므로 compareTo 메서드의 인수타입은 컴파일타임에 정해진다. 입력 인수의 타입을 확인하거나 형변환할 필요가 없다는 뜻이다. 인수의 타입이 잘못됐다면 컴파일 자체가 되지 않는다. 

 

  // 코드 14-3 비교자 생성 메서드를 활용한 비교자 (92쪽)
    private static final Comparator<PhoneNumber> COMPARATOR =
            comparingInt((PhoneNumber pn) -> pn.areaCode)
                    .thenComparingInt(pn -> pn.getPrefix())
                    .thenComparingInt(pn -> pn.lineNum);

      @Override
      public int compareTo(PhoneNumber pn) {
	        return COMPARATOR.compare(this, pn);
	    }

PhoneNumber명시하여 컴파일 시에 타입체크, thenComparingInt는 타입 추론 가능. Comparator는 수많은 보조 생성 메서드들로 중무장하고 있다.

 

정적 compare 메서드를 활용한 비교자

static Comparator<Object> hashCodeOrder = new Comparator<>(){
	public int compare(Object o1, Object o2){
    	return Integer.compare(o1.hashCode(), o2.hashCode());
    }
};

비교자 생성 메서드를 활용한 비교자 

static Comparator<Object> hashCodeOrder = 
	Comparator.comparingInt(o -> o.hashCode());