TIL/Java

[Java] Effective Java 3/E 3장 item 10~14

JoJobum 2023. 11. 14.

3장 모든 객체의 공통 메소드

item 10: equals는 일반 규약을 지켜 재정의하라

 

equals 메소드를 재정의하며서 발생할 수 있는 문제를 회피하는 방법은 재정의하지 않는 것

equlas 메소드를 재정의하지 않으면 오직 자기 자신과만 같게 된다.

equals 메서드를 재정의하지 않는 것이 최선인 Case

  1. 각 인스턴스가 본질적으로 고유하다.
  2. 인스턴스의 논리적 동치성을 검사할 일이 없다.
  3. 상위 클래스에서 재정의한 equals가 하위 클래스에도 딱 들어맞는다.
  4. 클래스가 private이거나 package-private이고 equals 메소드를 호출할 일이 없다.

equals 메소드를 재정의해야하는 Case

객체 식별성, 물리적으로 두 객체가 같냐? 가 아니라 논리적 동치성, 논리적으로 두 객체가 같냐? 로 동작해야 할 때

Ex) Integer, String 같은 값 클래스의 경우 두 객체가 물리적으로 같냐? 가 아니라 같은 값을 가지고 있냐? 가 equals 올바른 동작일 것이다. (보통 위와 같은 클래스는 논리적으로 같은 인스턴스가 2개 이상 만들어지지 않기에 논리적 동치성과 물리적 동치성이 같은 의미가 됨)

equals 메서드는 반드시 동치관계(equivalence relation)을 구현하며, 다음을 만족한다.

  1. 반사성 (reflexivity) : null이 아닌 모든 참조 값 x에 대해 x.equals(x) 는 true이다.
  2. 대칭성 (symmetry) : null이 아닌 모든 참조 값 x, y에 대해 x.equals(y)가 true이면 y.equals(x)도 true이다.
  3. 추이성 (transitivity) : null이 아닌 모든 참조 값 x, y, z에 대해 x.equals(y)가 true이고 y.equals(z)도 true이면, x.equals(z)도 true이다.
  4. 일관성 (consistency) : null이 아닌 모든 참조 값 x, y에 대해 x.equals(y)를 반복해서 호출하면 항상 true를 반환하거나 항상 false를 반환해야 한다.
  5. null 아님 : null이 아닌 모든 참조 값 x에 대하여 x.equals(null)은 false이다.

클래스가 불변이든 가변이든 equals의 판단에 신뢰할 수 없는 자원이 끼어들게 해선 안된다.

Ex) 주어진 url 과 매핑된 호스트의 ip 주소를 이용해 비교하는 경우, 결과가 가변적이라 결과가 항상 같다고 보장 못함 ⇒ 일관성X

equals() 메소드 재정의 방법

  1. == 연산자를 사용해 입력이 자기 자신의 참조인지 확인
  2. instance of 연산자로 입력이 올바른 타입인지 확인
  3. 입력을 올바른 타입으로 형변환한다.
  4. 입력 객체와 자기 자신의 대응되는 핵심 필드들이 모두 일치하는지 하나씩 검사

 

item 11: equals() 재정의 시 hashcode() 도 재정의 하라 

  • equals 비교에 사용되는 정보가 변경 되지 않았다면, hashcode 메소드는 같은 값을 반환해야 함
  • equals 가 두 객체를 같다고 판단하면, hashcode 는 같은 값을 반환해야 함
  • equals 가 두 객체를 다르다고 판단해도, hashcode가 다른 값을 반환할 필요는 없음 (다른 값 반환시 해쉬 테이블 성능 개선)
HashMap<Apple, String> m = new HashMap<>();
m.put(new Apple(”Red”), "빨간 사과");
m.get(new Apple("Red")); // "빨간 사과 X, null값 나옴)

논리적으로 같은 객체를 해쉬맵에 넣고 다시 꺼냈는데 null 값이 반환된다. 그 이유는 Apple 클래스는 hashcode가 재정의되지 않았기에 hashcode 를 비교하면 다른 값이 나와 다른 값 객체라고 판단함.

좋은 해시함수는 서로 다른 인스턴스들을 서로 다른 해시코드를 반환하는데 최대한 균일하게 분배하는 것

클래스가 불변이고 해시코드를 계산하는 비용이 크면 캐싱하는 방식 고려할 것

 

 

item12: toString을 항상 재정의하라

가진 주요 정보 모두 반환하자

toString에 담긴 주요 정보 받을 수 있는 api 만들 것 (toString 파싱하지 않게)

 

 

item13: clone 재정의는 주의해서 진행하라

clone 은 사실상 생성자랑 같은 효과를 냄

원본 객체에 영향을 주지 않는 동시에 복제된 객체의 불변식을 보장해야 함

cloneable은 여러 문제 파생시킴 ⇒ 복제 기능은 생성자와 팩토리를 사용하자

 

예외적으로 배열은 clone 메소드 방식이 가장 적합하다. 배열의 clone()은 런타임 타입과 컴파일 타입 모두가 원본 배열과 동일한 배열을 반환한다.

 

 

item14. Comparable을 구현할지 고려하라

Comparable 구현시,

Collection 관리 용이해짐

⇒ 순서가 명확한 값 클래스일시 구현하자

기존 클래스를 확장할 경우, 새로 추가한 값 컴포넌트를 포함하여 CompareTo 규약을 지킬 방법이 없음

⇒ 기존 클래스를 확장하는 것이 아니라 독립된 클래스를 만들고 이 클래스에 기존 클래스를 가리키는 필드를 두자

compareTo로 줄지은 순서와 equals의 결과가 같도록 하자

⇒ Collection, Set, Map 등에서 정의한 동작대로 동작할 수 있게 하기 위함이다.

 

반응형

'TIL > Java' 카테고리의 다른 글

[Java] Effective Java 3/E 3장 item 21~25  (0) 2024.01.20
[Java] Effective Java 3/E 4장 item 15~20  (0) 2024.01.18
[JAVA] Java Version 8 vs 17  (0) 2023.10.21
[Java] Effective Java 3/E item 7~9  (1) 2023.06.13
[Java] Effective Java 3/E item 4~6  (0) 2023.05.28

댓글