일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 | 31 |
Tags
- 자바
- 컴퓨터과학
- Effective Java
- 개발
- 뮤텍스
- github
- spring
- 스프링
- IT
- java
- OS
- 깃허브
- 신입
- 깃
- 스터디
- CS
- 프로그래밍
- 디지털
- 메모리
- package-private
- 우리카드
- 신입사원
- 이펙티브 자바
- 알고리즘
- 공채
- Public
- 컴퓨터공학
- 정보처리기사
- 운영체제
- 세마포어
Archives
- Today
- Total
주니어 개발자 성장기
아이템 14. Comparable을 구현할지 고려하라 본문
개요
Object
에 포함되는 인터페이스는 아니지만, 널리 사용될 수 있는 인터페이스이다.
Object.equals
에 더해서 순서까지 비교할 수 있으며 Generic을 지원한다.
CompareTo
규약
- 반사성
- 대칭성
- 추이성
- 일관성
권장
compareTo
가 0을 반환한다면 equals
도 true를 반환하도록 구현하는 것이 좋다. BigDecimal
클래스의 경우 equals
와 compareTo
의 결과가 다를 수도 있도록 구현되었다.
BigDecimal oneZero = new BigDecimal("1.0");
BigDecimal oneZeroZero = new BigDecimal("1.00");
System.out.println(oneZero.compareTo(oneZeroZero)); // Tree, TreeMap
System.out.println(oneZero.equals(oneZeroZero)); // 순서가 없는 콜렉션
결과(출력)
0
false
구현방법
Comparable<T>
를 구현하게 되면 T 타입과 비교할 수 있는 compareTo(T t)
메서드를 오버라이딩해야한다.
- 기본타입의 경우 박싱타입의
compare
메서드를 호출해서 구현해주자.
유의점
- 상속을 할 경우
equals
의 경우처럼 규약을 지키는 것과 다형성을 사용하는 것 중 양자택일을 해야한다.따라서 이런 경우에는 컴포지션을 쓰는 것을 책에서 권장한다. - 자바 8부터는
Comparator
인터페이스의 static 메서드를 활용해Comparator
인스턴스의 생성이 가능하다. 이를 통해 가독성이 좋은compareTo
를 만들 수 있다.
// 코드 14-3 비교자 생성 메서드를 활용한 비교자 (92쪽)
private static final Comparator<PhoneNumber> COMPARATOR =
comparingInt((PhoneNumber pn) -> pn.areaCode)
.thenComparingInt(pn -> pn.getPrefix())
.thenComparingInt(pn -> pn.lineNum);
@Override
public int compareTo(PhoneNumber pn) {
return COMPARATOR.compare(this, pn);
}
위 코드는 Comparator 를 static import 했다는 것에 주의하자. 성능이 10% 정도 느리다고 하지만, Comparator
가 엄청 자주 쓰이는 것이 아니라면 크게 개의치 않아도 된다.
그 외
Comparable
은 타입을 인수로 받는 제네릭 인터페이스이므로compareTo
메서드의 인수 타입은 컴파일타임에 정해진다. 입력 인수의 타입을 확인하거나 형변환할 필요가 없다는 뜻이다. - 이펙티브 자바 3판 90p
Comparable
이 제네릭 인터페이스이므로 매개 변수가 옳은 지를 컴파일 타임에 체크가 가능하다는 장점이 있다는 뜻이다.
자바의 타입 추론 능력이 이 상황에서 타입을 알아낼 만큼 강력하지 않기 때문에 프로그램이 컴파일되도록 우리가 도와준 것이다. - 이펙티브 자바 3판 92p
// 코드 14-3 비교자 생성 메서드를 활용한 비교자 (92쪽)
private static final Comparator<PhoneNumber> COMPARATOR =
comparingInt((PhoneNumber pn) -> pn.areaCode)
.thenComparingInt(pn -> pn.prefix)
.thenComparingInt(pn -> pn.lineNum);
위 코드에서 함께 언급된 내용으로 comparingInt
에서는 타입을 명시적으로 선언해야 했지만, 그 다음부터인thenComparingInt
에서는 타입추론이 잘 작동한다.
- 타입 추론은
var
로 변수선언을 할 때도 쓰인다. compareTo
에서 수를 비교할 경우 단순히 두 수를 더하거나 뺀 값으로 반환한다면 정수 오버플로(또는 언더플로)를 일으키거나 부동소수점 계산 방식에 따른 오류를 낼 수 있다. 따라서, 각 기본타입의 래퍼 클래스 (이를테면 Integer, String 등)가 제공하는 정적 메서드compare
를 이용하거나 비교자 생성 메서드를 사용하자.- 부동소수점 계산의 경우 부동소수점 타입(float, double)의 실제 표현이 지수부와 가수부로 나뉘는데, 가수부가 2진수로 표현되므로 정확한 표현이 어렵다. 특히, 계산을 하게되면 정보의 손실이 발생하기 때문에 정확한 계산이 안될 수 있다.
- 따라서, 소수점 아래를 내부적으로 Int로 만들어서 계산하는
BigDecimal
을 사용하자!
'Java > 이펙티브 자바' 카테고리의 다른 글
아이템 16. public 클래스에서는 public 필드가 아닌 접근자 메서드를 사용하라. (0) | 2023.09.10 |
---|---|
아이템 15. 클래스와 멤버의 접근 권한을 최소화하라. (0) | 2023.09.09 |
방어적 복사, 얕은 복사, 깊은 복사 (0) | 2023.09.02 |
아이템 13. Clone 재정의는 주의해서 진행하라. (0) | 2023.09.02 |
아이템 12. toString을 항상 재정의하라. (0) | 2023.09.02 |