* 전 page에 대해 공부하였으나, 모든 책의 내용을 다 적기 보다는 꼭 필요한 내용을 추려 이해하기로 하였음 !!! (많아서)
equals method란?
-> call by value로 객체 값을 비교하는 메소드 (==는 call by reference로 주소값 비교)
어떤 경우에 equals를 재정의하고, 하지 말아야 할까?
[equals를 재정의하지 않는 경우]
- 각 인스턴스가 본질적으로 고유한 경우 (값 클래스가 아닌 경우에 해당한다. ex) thread)
- 인스턴스의 논리적 동치성을 검사할 필요가 없는 경우
- 상위 클래스에서 재정의한 equals가 하위 클래스에도 딱 들어맞는 경우
- 클래스가 private이거나 package-private이고, equals method를 호출할 일이 없는 경우
- 인스턴스 통제 클래스와 같은 경우 (Enum, Singleton 등은 인스턴스가 한개만 만들어지기 때문에 논리적 동치성과 객체 식별성의 의미가 같다.)
[equals를 재정의하는 경우]
- 논리적 동치성을 확인해야 하는 경우이나, 상위 클래스의 equals가 논리적 동치성을 비교하도록 재정의되지 않았을 경우
(주로 값 클래스들이 해당됨 : Integer, String ...)
본인같은 경우에는 객체를 만들 때 특정 멤버변수를 통해 객체를 구별하고 싶을 때 equals를 재정의해서 사용함.
동치 관계 : 논리학이나 수학, 특히 집합론에서 쓰이는 도구 중 하나. 어떤 두 객체가 서로 "같다"는 개념을 추상화한다. 이항 관계의 일종.
[equals를 재정의 일반 규약]
-> 반드시 따라야 한다.
- 반사성(reflexivity)
/*객체는 자기 자신과 같아야 한다.*/
x.equlas(x) == true
public class item10 {
public static void main(String[] args) {
/*확인 방법*/
//인스턴스를 컬렉션에 넣어 contains를 호출해 true가 나오면 지킨 것!
List<Study> studyList = new ArrayList<>();
Study study = new Study(830, 4, "CSbreak");
studyList.add(study);
System.out.println(studyList.contains(study));
}
public static class Study{
int time;
int member;
String Teamname;
public Study(int time, int member, String Teamname) {
this.time = time;
this.member = member;
this.Teamname = Teamname;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Study study = (Study) o;
return time == study.time && member == study.member && Objects.equals(Teamname, study.Teamname);
}
@Override
public int hashCode() {
return Objects.hash(time, member, Teamname);
}
}
}
- 대칭성(symmetry)
/*서로에 대한 동치 여부에 똑같이 답해야 한다.*/
x.equals(y) == y.equals(x)
- 위의 예에서, Teamname이 같으면 (대소문자를 구분하지 않고) 같은 인스턴스라고 반환하는 객체가 있다고 정의해보자.
- equals가 일반 문자열과 비교를 시도하게 된다면, Study class의 String의 equals는 일반 String을 알고있지만, 일반 String의 equals는 Study class의 존재를 모른다. 따라서 두 equals method가 다른 값을 반환한다.
- 위의 예와 같이 collection에 넣어 contains를 호출해도 JDK 버전에 따라 다른 값을 반환한다.
//-> 해결 방안 : equals를 String과 Study class의 equals를연동하겠다는 꿈을 버린다.
@Override
public boolean equals(Object o) {
if (null == o) {
return false;
}
if (o instanceof Study) {
return Teamname.equalsIgnoreCase(((Study)o).Teamname);
}
return false;
}
- 추이성(transitivity)
x.equals(y) == true //이고
y.equals(z) == true //이면
x.equals(z) == true //이다.
- ex) 상위 클래스에 없는 새로운 필드를 하위 클래스에 추가하는 상황 (equls 비교에 영향)
- 구체 클래스를 확장해 새로운 필드를 추가하면서, 대칭성과 추이성을 모두 해결하며 equals를 재정의할 수 있는 방법 ?
- ==> 존재하지 않는다.
- 해결 방법 : 상속 대신 컴포지션을 사용한다. (item18) 상속하는 대신 하위 클래스의 private 필드로 두고, view 메서드를 public으로 추가하는 식으로 해결할 수 있다.
- 일관성(consistency)
x.equals(y) == true;
x.equals(y) == true;
// ...
x.equals(y) == true;
//언제나 값은 같아야 한다.
- 가변 객체의 경우 비교 시점에 따라 같을수도, 다를 수도 있는 반면, 불변 객체는 한번 다르면 끝까지 다르다.
- 클래스를 만들 때 불변 클래스로 만드는 것이 나을지 심사숙고하자.(item17)
- 가변/불변에 관계 없이, equals의 판단에 신뢰할 수 없는 자원이 껴있다면 일관성을 파괴할 수 있다.
- -> 해결 방법 :
- 항상 메모리에 존재하는 객체만을 사용하는 deterministic(결정적)계산만 수행해야 한다.
- Not null
모든 객체는 Null과 같지 않아야 한다.
x.equals(null) == false;
올바른 equals method 구현 방법
- == 연산자를 사용해 입력이 자기 자신의 참조인지 확인한다.
- 단순한 성능 최적화용으로 비교 작업이 복잡한 상황일 때 값어치를 한다.
- instanceof 연산자로 입력이 올바른 타입인지 확인한다.
- 묵시적 null 체크를 할 수 있다.
- Set, List, Map 등의 collection interface들이 해당된다.
- 입력을 올바른 타입으로 형변환한다.
- 2번에서 instanceof 검사를 했기 때문에 100% 성공한다.
- 입력 객체와 자기 자신의 대응되는 핵심 필드들이 모두 일치하는지 하나씩 검사한다.
- 2단계에서 인터페이스를 사용했다면 입력 필드값을 가져올 때 인터페이스 메소드를 사용해야 한다.
주의 사항
- 기본 타입은 == 으로 비교한다.
- double, float는 Double.compare(), Float.compare()을 이용해 검사해야 한다. -> 부동소수점을 다뤄야 하기 때문이다.
- 배열의 모든 원소가 핵심 필드이면 Arrays.equals 메서드들 중 하나를 사용한다.
- null이 정상 값으로 취급할 때는 OBjects.equals()를 이용해 NPE를 예방해야 한다.
- 비교하기 복잡한 필드를 가진 클래스는 표준형을 저장해둔 후 표준형을 비교한다.
- 최상의 성능을 바란다면 다를 가능성이 더 크거나 비교하는 비용이 싼 필드를 먼저 비교한다.
- 동기화용 락 필드 같이 객체의 논리적 상태와 관련 없는 필드는 비교하면 안 된다.
- 파생 필드가 객체 전체 상태를 비교하는 경우 파생 필드를 비교하는 것이 빠르다.
- equals를 재정의할 땐 hashCode도 반드시 재정의 한다.
- Object 타입을 매개변수로 받는 equals 메서드는 선언하지 말자.
'JAVA > Effective Java' 카테고리의 다른 글
item 12. toString을 항상 재정의하라 (0) | 2022.04.19 |
---|---|
item 11. equals를 재정의하려거든 hashCode도 재정의하라 (0) | 2022.04.19 |
item 9. try-finally 보다는 try-with-resources를 사용하라 (0) | 2022.04.16 |
item 8. finalizer과 cleaner 사용을 피하라. (0) | 2022.04.16 |
item7. 다 쓴 객체 참조를 해제하라 (0) | 2022.04.16 |
댓글