티스토리 뷰

compareTo 메서드

 Comparable 인터페이스는 compareTo 라는 제네렉 메서드를 단독으로 정의한다. compareTo 메서드는 단순 동시성 비교에 더해 순서까지 비교하는 스펙을 표현하고 있다. 이 인터페이스를 구현하는 것은 그 클래스의 인스턴스들에 자연적인 순서가 있음을 표현하는 행위가 된다. Comparable 인터페이스를 구현한 인스턴스들의 배열은 Arrays.sort(a) 유틸을 활용해 쉽게 정렬할 수 있다. 사실상 자바 라이브러리의 모든 값 클래스와 열거타입은 Comparable 을 구현하고 있다. 알파벳, 숫자, 연대 와 같이 순서가 명확한 값 클래스를 작성한다면 Comparable 인터페이스를 구현해야 한다.

compareTo 메서드의 일반 규약

1. 두 객체 참조의 순서를 바꿔 비교해도 예상한 결과가 나와야 한다

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

2. 첫 번째가 두 번째보다 크고, 두 번째가 세 번째 보다 크면, 첫 번째는 세 번째보다 커야한다

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

3. 크기가 같은 객체들끼리는 어떤 객체와 비교하더라도 항상 같아야 한다

  • 모든 z에 대해 x.compareTo(y) == 0 이면 sgn(x.compareTo(z)) == sgn(y.compareTo(z)) 이다

다음 권고는 필수는 아니지만 지키는게 좋다

  • (x.compareTo(y) == 0) == (x.equals(y)) 여야 한다.
  • CompareTo 를 구현하고 이 권고를 지키지 않은 모든 클래스는 그 사실을 명시해야 한다.

 일반 규약은 compareTo 메서드로 수행하는 동치성 검사에도 반사성, 대칭성, 추이성을 충족해야 함을 뜻한다. 이 때문에 기존 클래스를 확장한 구체 클래스에서 새로운 값 컴포넌트를 추가했다면 compareTo 규약을 지킬 방법이 없다. 확장하는 대신 독립된 클래스를 만들고, 이 클래스에 원래 클래스의 인스턴스를 가리키는 필드를 두는 우회법을 적용할 수 있다.

 

 BigDecimal 클래스는 compareTo, equals 메서드의 일관성을 보장하지 않는다. 따라서 new 연산자를 통해 동일한 값을 갖는 두 BigDecimal 인스턴스를 생성하면 HashSet 과 TreeSet 에서 각각 다르게 동작한다. (HashSet 은 원소 두 개로 인식, TreeSet 은 원소 한 개로 인식)

compareTo 메서드 작성

 Comparable 은 제네릭 인터페이스이므로, 메서드 인수타입이 컴파일 시점에 정해진다. 인수타입이 잘못되는 경우 컴파일 자체가 이루어지지 않아 작성에 용이하다. null 을 인수로 넣어 호출할 경우 NullPointerException 을 던져야 함에 유의하자. 객체 참조 필드를 비교할 때에는 compareTo 메서드를 재귀적으로 호출하자. Comparable 인터페이스를 사용할 수 없는 상황에서는 Comparator 를 대신 사용한다.

 

 클래스에 핵심 필드가 여러 개라면 어느 것을 먼저 비교하느냐가 중요해진다. 가장 핵심적인 필드부터 비교하면 성능측면에서 이점을 얻을 수 있다.

 

 Comparator 인터페이스는 수많은 보조 생성 메서드를 제공한다. 대표적으로 comparingInt, thenComparingInt 메서드가 있다. 이 메서드는 short 와 같은 더 작은 정수타입에 대해서도 함께 작동한다. double 버전 메서드를 사용하면 실수형 처리도 가능하다. 이와 같이 자바의 모든 숫자용 기본타입을 지원한다.

정리

 값 객체를 활용하는 프로그램에서는 Comparable 인터페이스의 중요성이 더욱 높아지게 된다. 책에서는 compareTo 메서드에서 필드의 값을 비교할 때 < 와, > 연산자를 사용하지 말 것을 권장하고 있다. 개인적으로 필자는 비교연산자를 사용하되 비교 결과값을 0으로 고정하는 방식을 추천한다. 예를 들어 다음과 같은 문장을 해석해보겠다.

if (x.compareTo(y) > 0) ...

 

 비교 결과값을 0으로 고정하면 0 앞의 연산자를 compareTo 문장의 자리로 가져오면 된다. 해석해보면 if (x > y) 인 상황이 되는 것이다. compareTo 문장을 사용하려는 의도는 메서드의 결과값이 1, 0, -1 임을 외우는 것이 아니라 비교를 하는 것이다. 결과값에 초점을 두기보다 비교연산자에 초점을 두면 사용하기가 더 편리할 것이다.

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday