Item 14. Comparable을 구현할지 고려하라

심규환·2022년 1월 23일
0

Effective Java

목록 보기
13/29
post-thumbnail

Comparable 인터페이스의 유일무이한 메서드인 compareTo를 살펴보자. copareTo는 두 가지 성격을 빼면 Object의 equals와 같다.
첫 번째는 값의 동치성 비교와 더불어 순서를 비교할 수 있다.
두 번째는 제네릭하다는 것이다.
만약 Comparable을 구현했다는 것은 클래스의 인스턴스간의 순서가 있다는 것을 의미한다.
그렇다면 Arrays.sort(a) 와 같이 정렬을 할 수 있게 된다.

다음 프로그램은 명령어로 입력된 인수들을 중복값을 제거하고 알파벳순으로 출력해준다.

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

이렇게 Comparable을 구현하면 수많은 제네릭 알고리즘과 컬렉션의 힘을 누릴 수 있게 된다.
다음은 CompareTo 메서드의 일반 규약이다. 이는 equals의 일반 규약과 비슷하다.

이 객체와 주어진 객체의 순서를 비교한다. 이 객체가 주어진 객체보다 작으면 음의 정수를 같으면 0을, 크면 양의 정수를 반환한다. 이 객체와 비교할 수 없는 타입의 객체가 주어지면 ClassCastException을 던진다.

  • x.compareTo(y) == -(y.compareTo(x)) 여야 한다.(x.compareTo(y)는 y.compareTo(x)가 예외를 더지면 똑같이 예외를 던져야 한다.
  • CompareTo을 구현한 클래스는 추이성을 보장해야 한다. 즉, (x.compareTo(y) > 0 && y.compareTo(z) > 0 이면 x.compareTo(z) 이여야 한다.
  • x.compareTo(y) == 0이면 sgn(x.compareTo(z)) == sgn(y.compareTo(z) 이다.
  • 이는 필수는 아니지만 지키는 것이 좋다. equals로 나오는 값과 compareTo로 나오는 값이 동일해야 한다.

equals 메서드는 모든 객체에 대해 동치관계를 부여하지만 compareTo는 타입이 다른 객체를 비교하지 않아도 된다.
compareTo 일반 규약을 지키지 못하면 순서를 이용하는 컬렉션의 사용이 어려워진다.

compareTo 메서드 작성 요령은 equals와 비슷하다. 몇 가지 차이점만 주의하면 된다. Comparable은 타입을 인수로 받는 제네릭 인터페이스이므로 compareTo 메서드의 인수 타입은 컴파일타임에 정해진다. 이는 입력 인수의 타입을 확인하거나 형변환할 필요가 없는 뜻이다.
인수 타입이 잘못됐다면 컴파일 자체가 되지 않는다. compareTo는 각 필드가 동치인지 비교하는 것이 아니라 그 순서를 비교한다.
compareTo 메서드에서 관계연산자 >와 <를 사용하여 비교하는 방식과 -를 사용하여 비교하는 방식은 거추장스럽고 오류를 유발하니 지양하기를 바란다.

다음은 자바의 정적 임포트 기능을 이용하여 정적 비교자 생성 메서드들을 그 이름만으로 사용하는 방식이다.

private static final Comparator<PhoneNumbe> COMPARATOR = 
comparingINT((PhoneNumber pn) -> pn.areaCode).thenComparingInt(pn -> pn.prefix).thenComparingInt(pn-> pn.lineNum);

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

코드를 한번 살펴보자. 처음 comparingINT는 객체 참조를 int 타입 키에 매핑하는 키 추출 함수를 인수로 받아, 그 키를 기준으로 순서를 정하는 비교자를 반환하는 정적 메서드이다. 쉽게 말해 pn.areaCode의 값을 Int 타입으로 받고 먼저 비교한 뒤, 만약 같다라고 나온다면 thenComparingInt를 사용하여 다음 값을 비교한다.
이처럼 thenComparingInt를 사용하여 우선 순위가 높은 것부터 비교를 순차적으로 하면 된다. 이는 값 타입을 비교할 때 사용하자.

객체 참조용 비교자 생성 메서드는 아래와 같이 하자.

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

Integer 클래스의 정적 메소드인 compare를 사용하여 hashcode를 비교한다.

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

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

profile
장생농씬가?

0개의 댓글