반응형
equals 재정의
equals를 재정의할 때는 논리적 동치성을 비교할 때이며 Object 명세에 적힌 규약을 따라줘야한다.
- 반사성
null이 아닌 모든 참조 값 x에 대해, x.equals(x)는 true다.
객체는 자기 자신과 같아야한다. - 대칭성
null이 아닌 모든 참조 값 x,y에 대해 x.equals(y)가 true면 y.equals(x)도 true이다.
두 객체는 서로에 대한 동치 여부에 똑같이 답해야 한다. - 추이성
null이 아닌 모든 참조 값 x,y,z에 대해, x.equals(y)가 true이고 y.equals(z)도 true면 x.equals(z)도 true다.
삼단논법을 생각하면 이해하기 쉽다. - 일관성
null이 아닌 모든 참조 값 x,y에 대해, x.equals(y)를 반복해서 호출하면 항상 true를 반환하거나 항상 false를 반환한다.
두 객체가 같다면 일관성 있게 쭉 같아야 하고 다르다면 쭉 달라야한다는 뜻이다. - null 아니여야 한다.
null이 아닌 모든 참조 값 x에 대해, x.equals(null)은 false다.
모든 객체가 null과 같지 않아야 한다.
equals 메서드 구현하는 단계
- ==연산자를 이용해 입력이 자기 자신의 참조인지 확인한다.
- instanceof 연산자로 입력이 올바른 타입인지 확인한다.
- 입력을 올바른 타입으로 형변환한다.
- 입력 객체와 자기 자신의 대응되는 필드들이 모두 일치하는지 하나씩 검사한다.
equals를 재정의할 때 hashCode도 재정의하자
hashCode를 재정의 하지 않으면 HashMap이나 HashSet 같은 컬렉션의 원소로 사용할 때 문제가 발생할 수 있다.
equals(Object)가 두 객체를 같다고 판단했다면, 두 객체의 HashCode는 같은 값을 반환해야 한다.
toString을 항상 재정의하자
처음 toString을 사용할때는 우리가 입력한 값이 출력될 것이라고 예상하지만, 실제로 사용해보면
[클래스이름@해시코드]가 출력되는 것을 확인할 수 있다.
때문에 우리는 toString을 재정의하여 우리가 바라는 적합한 문자열이 출력되게 하는 것이 바람직하다. 재정의한 toString은 프로그램은 쉽게 디버깅하게 해주며, 해당 객체에 관한 명확한 정보를 읽기 좋은 형태로 반환할 수 있게 해준다.
toString을 어떤 식으로 재정의할까?
- toString은 그 객체가 가진 주요 정보 모두를 반환하도록 재정의하자.
- toString을 재정의할 때 의도가 드러나도록 밝혀야 한다.
toString 재정의한 곳 위에 주석 처리를 하여 설명하는 것으로 충분하다.
Comparable 구현
Comparable 인터페이스는 메서드인 compareTo를 구현만 하면 된다.
Object의 메서드는 아니지만 성격이 비슷하다.
compareTo 메서드 규약
- 이 객체가 주어진 객체보다 작으면 음의 정수 반환
- 이 객체가 주어진 객체와 같으면 0 반환
- 이 객체가 주어진 객체보다 크면 양의 정수 반환
- 이 객체와 비교할 수 없는 타입이면 ClassCastException 발생
- Object의 equals 메서드와 마찬가지로 반사성, 대칭성, 추이성을 만족해야 한다.
compareTo 메서드에서 필드 비교시 정적 메서드 사용
compareTo 메서드 구현시 관계 연산자인 <, >를 사용하는 대신 Integer.compare, Double.compare 같은 정적 메서드를 사용하는 것을 권한다.
public int compareTo(Obecjt o) {
int result = Integer.compare(name, o.name);
if (result == 0) {
result = Integer.compare(age, o.age);
if (result == 0) {
result = Integer.compare(id, o.id);
}
}
return result;
}
값의 차를 이용한 compare는 지양하자
static Comparator<Object> hashCodeOrder = new Comparator<>() {
public int compare(Object o1, Object o2) {
return o1.hashCode() - o2.hashCode();
}
};
이 방식은 정수 오버플로를 일으키는 등의 오류를 낼 수 있다. 때문에 이는 위 예시에서 다음 두 방법으로 대체하는 것이 좋다.
/* *
* 정적 compare 메서드를 이용한 비교
*/
static Comparator<Object> hashCodeOrder = new Comparator<>() {
public int compare(Object o1 ,Object o2) {
return Integer.compare(o1.hashCode(), o2.hashCode());
}
};
/* *
* 비교자 생성 메서드를 이용한 비교
*/
static Comparator<Object> hashCodeOrder =
Comparator.comparingInt(o -> o.hashCode());
};
반응형
'Reading Book > 이펙티브 자바' 카테고리의 다른 글
[이펙티브 자바] Enum과 EnumMap (0) | 2022.12.12 |
---|---|
[이펙티브 자바] 클래스와 인터페이스를 유연하게 만드는 방법 (0) | 2022.12.12 |
[이펙티브 자바] try-finally 보다 try-with-resources 사용하자 (0) | 2022.12.12 |
[이펙티브 자바] 불필요한 객체 생성을 피하고, 다 쓴 객체 참조를 해제하자 (0) | 2022.12.11 |
[이펙티브 자바] 자원 직접 명시보다는 의존 객체 주입을 사용하기 (0) | 2022.12.11 |