- Comparable을 구현하는 것은 그 클래스의 인스턴스들에 자연적인 순서가 있음을 뜻한다.
- Comparable을 구현한 객체들의 배열은 손쉽게 정렬을 수행할 수 있다.
Arrays.sort(a);
- 검색, 극단값 계산, 자동 정렬되는 컬렉션 관리도 쉽게 수행할 수 있다.
- 이 외에도 수많은 제네릭 알고리즘과 컬렉션 활용이 가능하다.
- 자바 플랫폼 라이브러리 모든 값 클래스와 열거 타입이 Comparable을 구현한다.
public interface Comparable<T> {
int compareTo(T t);
}
- Comparable의 하나뿐인 메서드
- 두 가지만 제외하면 Object의 equals 와 같다.
- 단순 동치성 비교에 더해 순서까지 비교할 수 있다.
- 모든 객체에 전역 동치관계를 부여하는 equals 메서드와 달리, compareTo는 타입이 다른 객체를 신경 쓰지 않아도 된다.
- 타입이 다른 경우
- 예외를 던져서 처리
- 공통 인터페이스를 매개로 구현 객체들을 비교할 수도 있다.
- 타입이 다른 경우
- equals의 규약과 비슷하다.
- 주의사항도 같다.
- 기존 클래스를 확장한 구체 클래스에서 새로운 값 컴포넌트를 추가했다면, compareTo 규약을 지킬 방법이 없으므로 우회하여 처리한다.
- 확장 대신 독립된 클래스를 만들고, 원래 클래스의 인스턴스를 가리키는 필드를 둔다. (composition, 상속 대신 구성을 활용한다.)
- 내부 인스턴스를 반환하는 뷰 메서드를 제공한다.
- 바깥 클래스에 원하는 compareTo 메서드를 구현한다.
- 클라이언트는 바깥 클래스의 인스턴스를 필드 안에 담긴 원래 클래스의 인스턴스로 다룰 수 있다.
- 기존 클래스를 확장한 구체 클래스에서 새로운 값 컴포넌트를 추가했다면, compareTo 규약을 지킬 방법이 없으므로 우회하여 처리한다.
- 주의사항도 같다.
- 주어진 객체의 순서를 비교한다.
- 주어진 객체보다 작으면 음의 정수(-1), 크면 양의 정수(1), 같으면 0을 반환한다.
- 비교할 수 없는 타입의 객체가 주어지면, ClassCastException을 던진다.
- 이 규약을 지키지 못하면 비교를 활용하는 클래스와 어울리지 못한다.
- 정렬된 컬렉션, 검색과 정렬 알고리즘을 활용하는 유틸리티 클래스(Collections, Arrays)
-
반사성
- 두 객체 참조의 순서를 바꿔 비교해도 예상한 결과가 나와야 한다.
// 모든 x, y에 대해서 sgn(x.compareTo(y)) == -sgn(y.compareTo(x))
-
추이성을 보장해야 한다.
x.compareTo(y) > 0 && y.compareTo(z) > 0 // 이면 x.compareTo(z) > 0 //이다.
-
대칭성
- 크기가 같은 객체들끼리는 어떤 객체와 비교하더라도 항상 같아야 한다.
//모든 z에 대해 x.compareTo(y) == 0 //이면, sgn(x.compareTo(z)) == sgn(y.compareTo(z)) //이다.
-
필수는 아닌 권고 (강력 권장)
(x.compareTo(y) == 0) == (x.equals(y))
- Comparable을 구현하지만, 이 권고를 지키지 않은 클래스의 경우에는 그 사실을 명시해야 한다.
- 그러나 강력히 구현을 권장하며, 주의가 필요하다.
- 정렬된 컬렉션을 사용하는 경우, 해당 컬렉션이 구현한 인터페이스에 정의된 동작(equals 메서드 규약을 따른다.)과 엇박자를 낼 수 있기 때문이다.
- 정렬된 컬렉션은 동치성을 비교할 때, equals 대신 compareTo를 활용한다.
- equals 와 비슷하나 몇 가지 차이점을 갖는다.
- Comparable은 타입을 인수로 받는 제네릭 인터페이스이며, compareTo 메서드의 인수 타입은 컴파일 타임에 정해진다.
- 즉, 입력 인수의 타입 확인이나 형변환이 필요하지 않다.
- 타입이 잘못된 경우에 컴파일 자체가 실패한다.
- compareTo 메서드는 각 필드가 동치인지 비교하는 것이 아닌 순서를 비교하는 메서드이다.
- 객체 참조 필드의 비교를 위해 compareTo 메서드를 재귀적으로 호출한다.
- Comparable을 구현하지 않은 필드나 표준이 아닌 순서로 비교해야 한다면, 비교자 Comparator 를 대신 사용할 수 있다.
- 이 때, 비교자는 직접 만들거나 제공되는 것을 활용한다.
- 핵심 필드가 여러개라면, 가장 중요한 필드부터 비교해나가고, 비교 결과가 0이 아닌 값이 나오면, 추가 비교 없이 값을 반환하도록 한다.
- 자바 7 부터 박싱된 기본 타입 클래스들에 정적 메서드인 compare가 추가되어 이를 활용한다.
- 이전과 달리 정수 기본 타입 필드에서도 관계 연산자 < 와 > 를 사용하는 방식은 거추장스럽고 오류를 유발하니 지양한다.
- 자바 8에서는 Comparator 인터페이스가 일련의 비교자 생성 메서드와 함께 메서드 연쇄 방식으로 비교자를 생성할 수 이게 되었다.
- 그러나 약간의 성능 저하가 뒤따른다.
- 수많은 보조 생성 메서드들을 갖추고 있따.
- 객체 참조용 비교자 생성 메서드도 존재한다.
- comparing 이라는 정적 메서드 2개가 다중 정의되어 있다.
- thenComparing 이라는 인스턴스 메서드 3개가 다중 정의되어 있다.