아이템 14 : Comparable을 구현할지 고려하라
Comparable 인터페이스는 객체 간에 자연스러운 순서를 정의하는 방법을 제공하며, 클래스의 인스턴스들이 정렬 가능함을 의미한다. 이 인터페이스를 구현하면 compareTo 메서드를 통해 객체 간의 순서를 비교할 수 있게 된다. 순서가 명확한 VO(Value Object) 클래스를 작성할 때는 Comparable을 구현하는 것이 권장된다.
1. compareTo의 규약
compareTo 메서드는 객체의 순서를 비교하고 다음 값을 반환한다:
- 음의 정수: 이 객체가 주어진 객체보다 작을 때
- 0: 두 객체가 같을 때
- 양의 정수: 이 객체가 주어진 객체보다 클 때
compareTo는 다음의 규칙을 따라야 한다
- 두 객체의 순서를 바꿔서 비교해도 예상된 결과가 나와야 한다.
- 추이성을 만족해야 한다. 즉, A > B이고, B > C일 경우 A > C여야 한다.
- 크기가 같은 객체들끼리는 항상 같아야 한다.
- 가능하다면 equals와 동일한 동치성 결과를 가져야 한다. 즉, compareTo로 두 객체가 같다면 equals에서도 같아야 한다.
2. compareTo 작성 요령
compareTo 메서드를 작성하는 방법은 equals와 유사하나, 몇 가지 차이점이 있다:
- 제네릭 타입이므로 형변환이나 타입 확인이 필요 없다.
- equals는 동치성을 비교하지만, compareTo는 순서를 비교한다.
- 객체의 참조 필드를 비교할 때는 재귀적으로 compareTo를 호출하면 된다.
- Comparable을 구현하지 않은 필드나, 표준 순서와 다른 순서로 비교해야 할 경우에는 Comparator를 사용한다.
- JDK 7부터는 정수 타입 비교 시 관계 연산자 대신 **정적 메소드 compare()**를 사용하는 것이 권장된다.
핵심 필드부터 비교하라
비교는 중요한 필드부터 시작한다. 첫 번째 필드의 비교 결과가 즉시 결정된다면, 그 값을 바로 반환한다. 여러 필드가 있는 경우, 각 필드를 순차적으로 비교해 나간다.
public class PhoneNumber implements Comparable<PhoneNumber> {
private int areaCode;
private int prefix;
private int lineNum;
// compareTo 메서드
public int compareTo(PhoneNumber pn) {
// 먼저 지역번호를 비교하고, 같다면 접두사, 그 다음에 라인 번호를 비교
int areaCodeCompare = Integer.compare(this.areaCode, pn.areaCode);
if (areaCodeCompare != 0) {
return areaCodeCompare;
}
int prefixCompare = Integer.compare(this.prefix, pn.prefix);
if (prefixCompare != 0) {
return prefixCompare;
}
return Integer.compare(this.lineNum, pn.lineNum);
}
}
Comparator 사용법
Comparator는 기본 정렬 기준 외에 다른 기준으로 정렬할 때 유용하게 사용할 수 있다. JDK 8부터 Comparator는 람다 표현식과 다양한 메서드 체인을 제공하여 더 쉽게 사용할 수 있다.
private static final Comparator<PhoneNumber> COMPARATOR =
Comparator.comparingInt((PhoneNumber pn) -> pn.areaCode)
.thenComparingInt(pn -> pn.prefix)
.thenComparingInt(pn -> pn.lineNum);
public int compareTo(PhoneNumber pn) {
return COMPARATOR.compare(this, pn);
}
위의 코드는 체이닝 방식으로 PhoneNumber 객체를 비교한다. 먼저 areaCode를 비교하고, 그다음 prefix를 비교한 후, 마지막으로 lineNum을 비교한다.
핵심 정리
순서를 고려해야 하는 값 클래스를 작성한다면 꼭 Comparable 인터페이스를 구현하여, 그 인터페이스들을 쉽게 정렬하고, 검색하고, 비교 기능을 제공하는 컬렉션과 어우러지도록 해야 한다. compareTo 메서드에서 필드의 값을 비교할 때 < 와 > 연산자는 쓰지 말아야한다. 그 대신 박싱된 기본 타입 클래스가 제공하는 정적 compare 메서드나 Comparator 인터페이스가 제공하는 비교자 생성 메스드를 사용하자.
'Java' 카테고리의 다른 글
이펙티브 자바(Effective Java) 2장 - 아이템 13 clone 재정의는 주의해서 진행하라 (0) | 2024.12.03 |
---|---|
이펙티브 자바(Effective Java) 2장 - 아이템 12 toString을 항상 재정의하라 (0) | 2024.12.03 |
이펙티브 자바(Effective Java) 2장 - 아이템 11 equals를 재정의하려거든 hashCode도 재정의하라 (0) | 2024.12.03 |
이펙티브 자바(Effective Java) 2장 - 아이템 10 equals는 일반 규약을 지켜 재정의하라 (0) | 2024.12.03 |
이펙티브 자바(Effective Java) 2장 - 아이템 9 try-finally보다는 try-with-resources를 사용하라 (0) | 2024.12.03 |
아이템 14 : Comparable을 구현할지 고려하라
Comparable 인터페이스는 객체 간에 자연스러운 순서를 정의하는 방법을 제공하며, 클래스의 인스턴스들이 정렬 가능함을 의미한다. 이 인터페이스를 구현하면 compareTo 메서드를 통해 객체 간의 순서를 비교할 수 있게 된다. 순서가 명확한 VO(Value Object) 클래스를 작성할 때는 Comparable을 구현하는 것이 권장된다.
1. compareTo의 규약
compareTo 메서드는 객체의 순서를 비교하고 다음 값을 반환한다:
- 음의 정수: 이 객체가 주어진 객체보다 작을 때
- 0: 두 객체가 같을 때
- 양의 정수: 이 객체가 주어진 객체보다 클 때
compareTo는 다음의 규칙을 따라야 한다
- 두 객체의 순서를 바꿔서 비교해도 예상된 결과가 나와야 한다.
- 추이성을 만족해야 한다. 즉, A > B이고, B > C일 경우 A > C여야 한다.
- 크기가 같은 객체들끼리는 항상 같아야 한다.
- 가능하다면 equals와 동일한 동치성 결과를 가져야 한다. 즉, compareTo로 두 객체가 같다면 equals에서도 같아야 한다.
2. compareTo 작성 요령
compareTo 메서드를 작성하는 방법은 equals와 유사하나, 몇 가지 차이점이 있다:
- 제네릭 타입이므로 형변환이나 타입 확인이 필요 없다.
- equals는 동치성을 비교하지만, compareTo는 순서를 비교한다.
- 객체의 참조 필드를 비교할 때는 재귀적으로 compareTo를 호출하면 된다.
- Comparable을 구현하지 않은 필드나, 표준 순서와 다른 순서로 비교해야 할 경우에는 Comparator를 사용한다.
- JDK 7부터는 정수 타입 비교 시 관계 연산자 대신 **정적 메소드 compare()**를 사용하는 것이 권장된다.
핵심 필드부터 비교하라
비교는 중요한 필드부터 시작한다. 첫 번째 필드의 비교 결과가 즉시 결정된다면, 그 값을 바로 반환한다. 여러 필드가 있는 경우, 각 필드를 순차적으로 비교해 나간다.
public class PhoneNumber implements Comparable<PhoneNumber> { private int areaCode; private int prefix; private int lineNum; // compareTo 메서드 public int compareTo(PhoneNumber pn) { // 먼저 지역번호를 비교하고, 같다면 접두사, 그 다음에 라인 번호를 비교 int areaCodeCompare = Integer.compare(this.areaCode, pn.areaCode); if (areaCodeCompare != 0) { return areaCodeCompare; } int prefixCompare = Integer.compare(this.prefix, pn.prefix); if (prefixCompare != 0) { return prefixCompare; } return Integer.compare(this.lineNum, pn.lineNum); } }
Comparator 사용법
Comparator는 기본 정렬 기준 외에 다른 기준으로 정렬할 때 유용하게 사용할 수 있다. JDK 8부터 Comparator는 람다 표현식과 다양한 메서드 체인을 제공하여 더 쉽게 사용할 수 있다.
private static final Comparator<PhoneNumber> COMPARATOR = Comparator.comparingInt((PhoneNumber pn) -> pn.areaCode) .thenComparingInt(pn -> pn.prefix) .thenComparingInt(pn -> pn.lineNum); public int compareTo(PhoneNumber pn) { return COMPARATOR.compare(this, pn); }
위의 코드는 체이닝 방식으로 PhoneNumber 객체를 비교한다. 먼저 areaCode를 비교하고, 그다음 prefix를 비교한 후, 마지막으로 lineNum을 비교한다.
핵심 정리
순서를 고려해야 하는 값 클래스를 작성한다면 꼭 Comparable 인터페이스를 구현하여, 그 인터페이스들을 쉽게 정렬하고, 검색하고, 비교 기능을 제공하는 컬렉션과 어우러지도록 해야 한다. compareTo 메서드에서 필드의 값을 비교할 때 < 와 > 연산자는 쓰지 말아야한다. 그 대신 박싱된 기본 타입 클래스가 제공하는 정적 compare 메서드나 Comparator 인터페이스가 제공하는 비교자 생성 메스드를 사용하자.
'Java' 카테고리의 다른 글
이펙티브 자바(Effective Java) 2장 - 아이템 13 clone 재정의는 주의해서 진행하라 (0) | 2024.12.03 |
---|---|
이펙티브 자바(Effective Java) 2장 - 아이템 12 toString을 항상 재정의하라 (0) | 2024.12.03 |
이펙티브 자바(Effective Java) 2장 - 아이템 11 equals를 재정의하려거든 hashCode도 재정의하라 (0) | 2024.12.03 |
이펙티브 자바(Effective Java) 2장 - 아이템 10 equals는 일반 규약을 지켜 재정의하라 (0) | 2024.12.03 |
이펙티브 자바(Effective Java) 2장 - 아이템 9 try-finally보다는 try-with-resources를 사용하라 (0) | 2024.12.03 |