public boolean equals(Object obj) {
return (this == obj);
}
특히 아래와 같은 경우에는 재정의하지 않는 것이 최선!
값을 표현하는 것이 아니라 동작하는 개체를 표현하는 클래스라면
예를 들어 Thread 클래스! Object의 equals로 충분!
논리적 동치성(logical equality)을 검사할 일이 없다면... 근데 논리적 동치가 뭘까요?
두 개의 명제 p, q의 쌍방조건 p <-> q 가 항진명제이면 두 명제 p, q는 논리적 동치라고 한다.
항진명제
항진명제? 각 명제의 참/거짓의 모든 조합에 대해서 항상 참인 것!
"자유가 아니면 차라리 죽음을 달라!"는 말은 "자유 혹은 죽음을 달라!" 와 같다.
여기서 p가 자유가 아니다. q가 죽음을 달라.
p와 q가 true일 때,
p->q / 자유가 아니다면 죽음을 달라 : true
p^(p->q) / 자유가 아니다 AND (자유가 아니다면 죽음을 달라) : true
(p^(p->q))->q / 자유가 아니다 AND (자유가 아니라면 죽음을 달라) 면 죽음을 달라 : true
p가 true이고 q가 false일 때,
p -> q / 자유가 아니다면 죽음을 달라 : false
p^(p->q) / 자유가 아니다 AND (자유가 아니다면 죽음을 달라) : false
(p^(p->q))->q / 자유가 아니다 AND (자유가 아니라면 죽음을 달라) 면 죽음을 달라 : true
// 값을 비교하려면 해야해요.
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}
하지만 enum 같은 경우는 재정의하지 않아도 됩니다.
어차피 논리적으로 같은 인스턴스가 2개 이상 생성되지 않아요.
논리적 동치성과 객체 식별성이 사실상 똑같으니까요.
equals 메서드는 동치 관계를 구현하며, 아래의 조건을 만족한다.
반사성(reflexivity) : null이 아닌 모든 참조 값 x에 대해, x.equals(x)는 true다.
이건 위반하기 더 어려울 것 같음.
대칭성(symmertry) : null이 아닌 모든 참조 값 x, y에 대해, x.equals(y)가 true면 y.equals(x)도 true다.
// 그래도 위반해보고 싶다면!
public final class CaseInsensitiveString {
private final String s;
public CaseInsensitiveString(String s) {
this.s = Objects.requireNonNull(s);
}
@Override
public boolean equals(Object o) {
if( o instanceof CaseInsensitiveString) {
return s.equalsIgnoreCase((CaseInsensitiveString) o).s);
}
if ( o instanceof String) {
return s.equalsIgnoreCase((String) o);
}
}
}
// 실행은?
CaseInsensitiveString cis = new CaseInsensitiveString("Media");
String s = "media";
cis.equals(s); // true
s.equals(cis); // false
- **추이성(transitivity)** : null이 아닌 모든 참조 값 x, y, z에 대해, x.equals(y)가 true이고 y.equals(x)도 true면 x.equals(z)도 true 다.
- **일관성(consistency)** : null이 아닌 모든 참조 값 x, y에 대해, x.equals(y)를 반복해서 호출하면 항상 true를 반환하거나 항상 false를 반환한다.
- **null이 아니다** : null이 아닌 모든 참조 값 x에 대해, x.equals(null)은 false다.
```java
// null일 때는 false!
@Override
public boolean equals(Object o) {
if(o == null) {
return false;
}
// ... 생략
}
그래도 구현해야 겠다면?
== 연산자를 사용해 입력이 자기 자신의 참조인지 확인한다.
그렇다면 true를 반환한다.
instanceof 연산자로 입력이 올바른 타입인지 확인한다.
그렇지 않다면 false를 반환한다.
입력이 올바른 타입으로 형변환한다.
위에서 검사했으므로 100% 성공할 수 있다.
입력된 객체와 자기 자신의 대응되는 '핵심' 필드들이 모두 일치한지 비교한다.
모든 필드가 일치하면 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;
}
}
먼저 결론,
항진명제
equals 메서드는 동치 관계를 구현하며, 아래의 조건을 만족한다.
대칭성(symmertry) : null이 아닌 모든 참조 값 x, y에 대해, x.equals(y)가 true면 y.equals(x)도 true다.
// 실행은? CaseInsensitiveString cis = new CaseInsensitiveString("Media"); String s = "media";
cis.equals(s); // true s.equals(cis); // false
그래도 구현해야 겠다면?
== 연산자를 사용해 입력이 자기 자신의 참조인지 확인한다.
instanceof 연산자로 입력이 올바른 타입인지 확인한다.
입력이 올바른 타입으로 형변환한다.
입력된 객체와 자기 자신의 대응되는 '핵심' 필드들이 모두 일치한지 비교한다.
아래와 같은 방법으로...
[equals를 재정의할 때는 hashCode도 반드시 재정의해야 한다.] (https://github.com/NMP-Study/EffectiveJava/issues/11)
File 클래스라면 심볼릭 링크를 비교해 같은 파일을 가리키는 지 확인하는 행동은 안된다.
Object 외의 타입을 매개변수로 받는 equals는 안된다! (오버라이드도 안된다)
결론은... 정말 특이한 상황이 아니라면 하지 않는게 좋아보인다.