티스토리 뷰
728x90
반응형
Comparable을 구현할지 고려하라
- 객체간 정렬이 필요한 경우 사용할 수 있는 인터페이스는 Comparator, Comparable이 있습니다.
- 여기서 Comparable 인터페이스는 compareTo라는 하나의 메서드를 정의하고 있으며, 이 메서드의 성격은 Object의 equals 메서드와 비슷한데, 동치성 뿐만 아니라 객체의 순서까지 비교가능하고 제네릭하다는 차이점이 있습니다.
- Comparable을 구현했다는 것은 그 클래스의 인스턴스들에는 자연적인 순서가 있음을 의미합니다.
- 알파벳, 숫자, 연대 같이 순서가 명확한 값 클래스를 작성한다면 반드시 Comparable 인터페이스를 구현하는게 좋습니다.
compareTo 메서드 규약
- 해당 객체와 주어진(매개변수) 객체의 순서를 비교합니다.
- 해당 객체가 더 크면 양수를 반환합니다.
- 해당 객체가 더 작으면 음수를 반환합니다.
- 해당 객체와 주어진 객체가 같을 경우 0을 반환합니다.
- 해당 객체와 비교할 수 없는 타입의 객체가 전달되면 ClassCastException이 발생하게 됩니다.
💡 대칭성을 보장해야 합니다.
- 모든 x, y에 대해 sgn(x.compareTo(y)) == -sgn(y.compareTo(x)) 여야 합니다.
- 예외도 x.compareTo(y)와 y.compareTo(x)가 동일하게 발생해야 합니다.
💡 추이성을 보장해야 합니다.
- 객체 x, y, z가 있다고 가정했을 때
- x.compareTo(y)가 양수이고, y.compareTo(z)도 양수라면 x.compareTo(z)도 양수여야 합니다.
- x.compareTo(y)가 0일 때 sgn(x.compareTo(z)) == sgn(y.compareTo(z))여야 합니다.
- x == y일때 x == z && y == z여야 한다는 의미입니다.
- (x.compareTo(y) == 0) == (x.equals(y))는 꼭 지켜야하는 것은 아니지만 권고 사항입니다.
- equals는 논리적 동치성을 판단하는 기준이 같다는 의미입니다.
- 이를 지키지 않는 것은 이 클래스의 순서는 equals 메서드와 일관되지 않습니다.
- 이를 지키지 않으면 Collection, Map, Set에서 정의된 동작과 엇박자를 낼 수 있습니다. 정렬 컬렉션은 동치를 비교할 때 equals 대신 compareTo 메서드를 사용합니다.
Comparable 사용 방법
- Comparable 인터페이스는 타입을 인수로 받는 제네릭 인터페이스라서 compareTo 메서드의 인수 타입은 컴파일 타임에 정해집니다. 그렇기 때문에 타입을 확인하거나 형변환을 할 필요가 없습니다.
- compareTo 메서드는 각 필드의 동치관계를 보는게 아니라 순서를 비교합니다.
- 객체 참조 필드를 비교하려면 compareTo 메서드를 재귀적으로 호출합니다.
- Comparable을 구현하지 않은 필드나 표준이 아닌 순서로 비교해야하는 경우에는 Comparator를 사용하면 됩니다.
- 아래 예제에서는 Comparable 인터페이스를 구현함으로써 Set과 Collections.sort 라는 멋진 정렬 기능을 자유롭게 사용할 수 있습니다.
@ToString
public class CaseInsensitiveString implements Comparable<CaseInsensitiveString> {
private String s;
public CaseInsensitiveString(String s) {
this.s = s;
}
@Override
public int compareTo(CaseInsensitiveString o) {
return String.CASE_INSENSITIVE_ORDER.compare(s, o.s);
}
}
public class EffectiveJavaApplication {
public static void main(String[] args) throws CloneNotSupportedException {
CaseInsensitiveString cis1 = new CaseInsensitiveString("Apple");
CaseInsensitiveString cis2 = new CaseInsensitiveString("blue");
CaseInsensitiveString cis3 = new CaseInsensitiveString("abuse");
CaseInsensitiveString cis4 = new CaseInsensitiveString("Cream");
TreeSet<CaseInsensitiveString> cisSet = new TreeSet<>();
cisSet.add(cis1);
cisSet.add(cis2);
cisSet.add(cis3);
cisSet.add(cis4);
System.out.println(cisSet.toString());
ArrayList<CaseInsensitiveString> cisList = new ArrayList<>();
cisList.add(cis1);
cisList.add(cis2);
cisList.add(cis3);
cisList.add(cis4);
Collections.sort(cisList);
System.out.println(cisList.toString());
}
}
// 결과
// [CaseInsensitiveString(s=abuse), CaseInsensitiveString(s=Apple), CaseInsensitiveString(s=blue), CaseInsensitiveString(s=Cream)]
// [CaseInsensitiveString(s=abuse), CaseInsensitiveString(s=Apple), CaseInsensitiveString(s=blue), CaseInsensitiveString(s=Cream)]
💡 필드값의 중요도에 따라 우선순위로 비교할 수 있습니다.
public class PhoneNumber implements Comparable<PhoneNumber> {
private final int first;
private final int second;
private final int third;
public PhoneNumber(int first, int second, int third) {
this.first = first;
this.second = second;
this.third = third;
}
@Override
public int compareTo(PhoneNumber o) {
int result = Integer.compare(this.first, o.first);
if (result == 0) {
result = Integer.compare(this.second, o.second);
if (result == 0) {
result = Integer.compare(this.third, o.third);
}
}
return result;
}
}
💡 정리
- 순서를 고려해야하는 값 클래스는 Comparable 인터페이스를 구현하는게 좋습니다.
- compareTo 메서드에서는 비교연산자(<. >)를 사용하지 않아야 합니다.
- compareTo 메서드를 구현할 때 박싱 클래스에서 제공하는 compare 메서드를 적극 활용하는게 좋습니다.
참고자료)
https://jake-seo-dev.tistory.com/32?category=906605
728x90
반응형
'스터디 > 이펙티브 자바' 카테고리의 다른 글
이펙티브 자바 - Item16. public 클래스에서는 public 필드가 아닌 접근자 메서드를 사용하라. (0) | 2022.07.11 |
---|---|
이펙티브 자바 - Item15. 클래스와 멤버의 접근 권한을 최소화하라. (0) | 2022.07.11 |
이펙티브 자바 - Item13. clone 재정의는 주의해서 진행하라. (0) | 2022.07.09 |
이펙티브 자바 - Item12. toString을 항상 재정의하라. (0) | 2022.07.08 |
이펙티브 자바 - Item11. equals를 재정의하려거든 hashCode도 재정의하라. (0) | 2022.07.07 |
공지사항
최근에 올라온 글
최근에 달린 댓글
- Total
- Today
- Yesterday
TAG
- service based architecture
- pipe and filter architecture
- spring boot redis 대기열 구현
- polling publisher spring boot
- 자바 백엔드 개발자 추천 도서
- pipeline architecture
- 트랜잭셔널 아웃박스 패턴 스프링 부트 예제
- 트랜잭셔널 아웃박스 패턴 스프링부트
- 서비스 기반 아키텍처
- spring boot excel download paging
- spring boot excel download oom
- 레이어드 아키텍처란
- java userThread와 DaemonThread
- space based architecture
- spring boot poi excel download
- spring boot redisson destributed lock
- JDK Dynamic Proxy와 CGLIB의 차이
- redis 대기열 구현
- spring boot 엑셀 다운로드
- microkernel architecture
- transactional outbox pattern spring boot
- java ThreadLocal
- redis sorted set
- spring boot redisson sorted set
- transactional outbox pattern
- spring boot redisson 분산락 구현
- 공간 기반 아키텍처
- redis sorted set으로 대기열 구현
- @ControllerAdvice
- 람다 표현식
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
글 보관함