본문 바로가기

Dev Book Review/Effective Java

[Effective Java] item 14. Comparable을 구현할지 고려하라

1. compareTo()와 equals()의 차이

compareTo는 Object의 메서드가 아니다.

성격은 두가지만 빼면 Object의 equals와 같다.

  • compareTo는 단순 동치성 비교에 더해 순서까지 비교 가능하다.
  • 그 클래스의 인스턴스들에 자연적인 순서가 있음을 뜻할 수 있다.

그래서 Comparable을 구현한 객체들의 배열은 손쉬운 정렬이 가능하다.
알파벳, 숫자, 연대 등 순서가 명확한 값 클래스 작성할땐 구현하자.

 

2. CompareTo() 메서드 규약

equals와 같은 내용이다 (주의점, 우회법 모두 같다.)

this < object : -1
this == object : 0
this > object : 1

 

ㄱ. 반사성, 대칭성, 추이성을 충족해야한다

sgn(x.compareTo(y) == -sgn(y.compareTo(x))

x.compareTo(y)y.compareTo(x)가 예외를 던질 때에 한해 예외를 던진다.
두 객체 참조의 순서를 바꾸어 비교해보아도 예상한 결과가 나와야한다.

x.compareTo(y) > 0 && y.compareTo(z) > 0이면 x.compareTo(z) > 0 이다.

첫번째가 두번째보다 크고 두번째가 세번째보다 크면, 첫번째는 세번째보다 크다.

 Comparalbe을 구현한 클래스는 모든 z에 대해 x.compareTo(y) == 0 이면 sgn(x.compareTo(z)) == sgn(y.compareTo(z)) 이다.

크기가 같은 객체들끼리 어떤 객체와 비교해도 항상 같아야한다.

(x.compareTo(y) == 0) == (x.equals(y)) 여야 한다.

compareTo로 수행한 동치성 테스트의 결과가 equals와 같아야한다.
지키지 않을 때는 명시해야한다. "주의 : 이 클래스의 순서는 equals 메서드와 일관되지 않는다."

컬렉션 구현 인터페이스(Collection, Set, Map) - 구현에 따른 주의가 필요하기 때문이다.
: equals 메서드 규약을 따른다 되어있다.
: 정렬된 컬렉션들은 동치성 비교시 equals대신 compareTo 사용

 

ㄴ. 기존 클래스를 확장한 구체클래스에서 새로운 값 컴포넌트를 추가하면 compareTo 지킬 방법이 없다.

우회법 : 컴포지션을 사용하고 + '뷰' 메서드를 제공하자

 

3. compareTo 메서드 작성요령

equals와의 차이점만 주의하면 된다.

 Comparable은 타입을 인수로 받는 제네릭 인터페이스이다 : 메서드의 인수타입은 컴파일타임에 정해진다.

 compareTo 메서드는 필드의 동치가 아니라 순서를 비교한다.Comparable을 구현하지 않았다면, Comparator를 사용할 수 있다.

 compareTo 메서드 구현시 관계연산자 <, > 사용하는 방식은 거추장 스럽고 오류를 유발한다. [Java7]

// 아래 방법을 사용하자.
Integer.compare(a,b);
Float.compare(a,b);
Double.compare(a,b);

   클래스의 핵심필드 여러개중 어떤것을 먼저 비교할 지에 대해 집중하라

 비교자 생성 메서드(comparator construction method)와 팀을 꾸려 메서드 연쇄로 비교자를 생성. [Java 8]

private static final Comparator<PhoneNumber> COMPARATOR =
  comparingInt((PhoneNumber pn)->pn.areaCode)	// Comparator의 인스턴스 메서드
  	.thenComparingInt(pn -> pn.prefix)			// 원하는 만큼 연달아 호출 가능
		.thenComparingInt(pn -> pn.lineNumber);

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

    이 람다에서 입력 인수의 타입을 명시해 주었다. (프로그램 컴파일을 도와준 것과 같다.)

 Comparator의 보조 생성 메서드

  comparingLong, thenComparingLong
  comparingDouble, thenComparingDouble

 값의 차를 이용한 compareTo, compare 메서드를 사용하지 말자.

  정수 오버플로 / 부동 소수점 계산 방식 오류  
  개선 : Integer.compare(a,b); || Comparator.comparingInt(x -> x.hashCode())