equals 메서드를 재정의해서 사용하는 경우가 많은 아래의 경우엔 기본 equals 메소드를 사용하는 걸 추천한다.
1) 각 인스턴스가 본질적으로 고유한 경우 (thread -> 동작 클래스)
2) 인스턴스의 논리적 동치성을 검사할 일이 없는 경우
3) 상위클래스에서 재정의한 equals 가 하위 클래스에도 맞는 경우
4) 클래스가 private이거나 package-private이고 equals 메서드를 호출할 일이 없는 경우
하지만 equals 메서드를 재정의해야 된다면 아래의 규약을 따라야 한다.
1) 반사성 : 자기자신에 대한 비교 = 항상 참
2) 대치성 : 서로 비교 대상을 바꾸더라도 = 항상 참
3) 추이성 : 서로 참이라면 연결관계에 있는 제3의 객체도 참이여야 한다.
추이성을 보장하는 로직을 작성하기 어려운데 '상속 대신 컴포지션을 사용'하는 방법을 사용할 수 있다.
public class InheritanceColorPoint {
private final Point point;
private final Color color;
public InheritanceColorPoint(int x, int y, Color color) {
point = new Point(x, y);
this.color = Objects.requireNonNull(color);
}
//이 InheritanceColorPoint에서 Point 뷰를 반환한다.
public Point asPoint() {
return point;
}
public boolean equals(Object o) {
if(!(o instanceof InheritanceColorPoint)) {
return false;
}
InheritanceColorPoint cp = (InheritanceColorPoint) o;
return cp.point.equals(point) && cp.color.equals(color);
}
}
4) 일관성 : 처음 참이면 항상 참이여야 한다.
5) null 아님 : 널이 아니여야 한다.
만약 위 규약을 어길 경우 equals 동작이 정상적임을 보장할 수 없다.
equals를 구현했다면, 딱 3가지만 테스트 코드로 확인하자!!
1. 대칭적인가?
2. 추이성이 있는가?
3. 일관적인가?
그리고 equals를 재정의할때 hashCode도 반드시 재정의해야 된다. (아이템 11)
[equals 재정의 절차]
1) == 연산자를 이용하여 입력이 자기 자신의 참조인지 확인해야 한다.
=> 자신의 참조라면 true를 반환해야 한다.
2) instanceof 연산자로 입력된 변수가 올바른 타입인지 확인해야 한다. 그렇지 않다면 false를 반환한다.
=> 입력을 올바른 타입으로 형변환한다. 위에서 타입을 검사했으므로 무조건 성공하게 됩니다.
3) 입력된 객체와 자기 자신의 대응되는 핵심 필드들이 모두 일치한지 비교한다.
=> 모든 필드가 일치하면 true, 그렇지 않다면 false를 반환한다.
float와 double을 제외한 기본 타입(primitive type)은 == 연산자로 비교하고
float와 double은 부동 소숫점 등을 위해 Float.compare, Double.compare로 비교한다.
참조 타입 필드의 경우는 각각의 equals 메서드로 비교한다.
public final class phoneNumber {
private final short areaCode, prefix, lineNum;
@Override
public boolean equals(Object o) {
if( o == this) {
return true;
}
if( o == null) {
return false;
}
if(!(o instanceof PhoneNumber)) {
return false;
}
PhoneNumber pn = (PhoneNumber)o;
return pn.lineNum == lineNum && pn.prefix == prefix
&& pn.areaCode == areaCode;
}
}
[주의 사항]
중요한 부분은 Object 외에 타입을 매개변수로 받는 equals 메서드 작성은 안된다. 물론 오버라이드도 안된다. File 클래스인 경우는 심볼릭 링크(symbolic link)를 비교하여 같은 파일을 가리키는 지 확인하는 행동도 위험하다.
한가지 팁은 equals 메서드 재정의를 꼭 직접 할 필요는 없다. IDE에서 제공하는 기능을 이용하거나 AutoValue 프레임워크를 이용해도 된다. 오히려 이 방법이 재정의할 경우 발생하는 이슈를 더 줄여 줄 것이다.
'IT > 자바' 카테고리의 다른 글
[Effective Java] Item 15 클래스와 멤버의 접근 권한을 최소화하라 (0) | 2021.11.20 |
---|---|
[Effective Java] Item 14 Comparable을 구현할지 고려하라 (0) | 2021.11.11 |
[Effective Java] item 8, 9 finalizer & cleaner 사용을 피해라 / try-finally 보다는 try-with-resources를 사용해라 (0) | 2021.07.25 |
[Effective Java] item 7 다 쓴 객체 참조를 해제하라 (0) | 2021.07.25 |
[Effective Java] item6 불필요한 객체 생성을 피하라 (0) | 2021.07.25 |