- equals는 일반 규약을 지켜 재정의하라2021년 03월 29일 16시 07분 32초에 업로드 된 글입니다.작성자: jCurve728x90반응형
우선 필요한 경우가 아니면 equals를 재정의하지 말고 필요하다면 equals와 hashCode는 왠만하면 인텔리제이나 다른 IDE에서 제공해주는 오버라이딩을 사용하자,,
클래스의 equals를 재정의하지 않는 것이 좋은 상황은 언제일까?
- 각 인스턴스가 본질적으로 고유하다.
- 인스턴스의 '논리적 동치성'을 검사할 일이 없다.
- 상위 클래스에서 재정의한 equals가 하위 클래스에도 딱 들어 맞는다.
- 클래스가 private이거나 package-private이고 eqauls 메서드를 호출할 일이 없다
각 인스턴스가 본질적으로 고유하다는 말은 해당 인스턴스가 어떠한 값을 표현하기 위한 목적이 아니라 동작하는 인스턴스를 표현하는 클래스에 해당한다.
'논리적 동치성'이란 어떠한 문장 P와 Q가 존재하며 P가 Q의 논리적인 결과로 이끌어질 수 있으며 , Q 또한 P의 논리적인 결과가 될 수 있을때 '논리적 동치'라 한다.
'실질적 동치성'도 존재하는데 이는 P와 Q간에 필요충분조건이 성립하여 P=Q이고 Q=P인 경우 '실질적 동치'라 한다.
논리적 동치는 실질적 동치이지만 그 역은 성립하지 않는다.인스턴스의 '논리적 동치성'을 검사할 일이 없다는 말은 정규표현식에서 2개의 Pattern 인스턴스를 P,Q로 정의할 때 각각의 논리적인 결과가 상대 인스턴스의 논리적인 결과로 결국 귀결되는지 검사하는데 사용할 필요가 없다면 Object의 기본 equals로 해결하는 것이 옳다.
결국 equals를 재정의 해야하는 상황은..
객체가 앞서 말한 실질적 동치성이 같은지를 판단할때가 아니라 어떠한 방식으로 논리적인 결과가 도출되는데 해당 결과가 동일해야하며, 상위 클래스의 equals가 현재 필요한 논리적인 결과를 도출하는 방식으로 재정의되어 있지 않을 때다.
이러한 조건에 해당되는 클래스의 예로 보통 Wrapper클래스가 있는데 Integer와 String으로 표현한 값이 동일한지를 판단하기 위해 equals를 재정의한다.
이러한 클래스들도 인스턴스 생성시 값이 같은 인스턴스가 둘 이상 만들어지지 않음을 보장하는 통제 클래스라면 equals를 재정의 하지 않아도 된다. 그러면 어차피 논리적으로 주소값이 같은 인스턴스가 2개 이상 만들어지지 않기 때문에 논리적 동치성이 보장된다.
Object에 명세된 equals 재정의시 일반 규약
- 반사성: null이 아닌 모든 참조 값 x에 대해, x.equals(x)는 true다
- 대칭성: null이 아닌 모든 참조 값 x,y에 대해, x.equals(y)가 true이면 역이 성립한다.
- 추이성: null이 아닌 모든 참조 값 x,y,z에 대해 , x.equals(y)가 true이고 y.equals(z)가 true이면 x.equals(z)는 true이다.
- 일관성: null이 아닌 모든 참조 값 x,y에 대해, x.equals(y)를 반복해서 호출하면 항상 true or false 반환
- not null: null이 아닌 모든 참조 값 x에 대해, x.equals(null)은 false다.
동치관계란 집합을 서로 같은 원소들로 이뤄진 부분집합으로 나누는 연산이며, 이 부분집합을 동치류라 한다.
위의 규약을 만족시키면 모든 원소가 같은 동치류에 속한 어떤 원소와도 서로 교환이 가능해야 한다.
대칭성은 두 객체는 서로에 대한 동치 여부에 똑같이 답해야 하는것으로 위 규약 처럼 equals와 그 역의 결과가 동일해야 한다.
두 클래스의 객체를 equals로 동치 비교 결과 일반 규약중 대칭성을 위배하는 것을 볼 수 있다.
위와 같은 equasls 규약을 어기면 해당 객체를 사용하는 다른 객체에서 어떻게 반응할지 알 수 없다.
따라서 CaseInsensitiveString의 equals를 재정의해서 String과 연동하려하면 안된다.
추이성의 예도 한번 보자.
여기서 문제가 있다.
대칭성과 추이성을 둘 다 만족시키며 equals를 재정의할 수 가 없다.
구체 클래스를 확장해 새로운 값을 추가하면서 equals 규약을 만족시킬 방법은 존재하지 않는다.
인스턴스를 검사하는게 아니라 동등 클래스인지 비교하는 것으로 equals로 바꾸는것은 리스코프 치환 원칙에 위배되어 할 수 없다.
이 원칙을 어긴다면 Set과 같은 컬렉션 구현체에서 내부적으로 해당 값이 존재하는지 확인할 때 equals를 사용하는데 instanceof가 아니라 getClass를 사용할 경우 상위 클래스가 입력된 상태에서 하위 클래스가 들어가게 되면 false를 반환하게 될 것이다.
equals 메서드 구현 방법
- == 연산자를 사용해 입력이 자기 자신의 참조인지 확인
- instanceof 연산자로 입력이 올바른 타입인지 확인
- 입력을 올바른 타입으로 형변환
- 입력 객체와 자기 자신의 대응되는 '핵심' 필드들이 모두 일치하는지 하나씩 검사
반응형'JAVA' 카테고리의 다른 글
Hash (0) 2021.04.10 Listener 와 Adapter (0) 2021.04.10 try-finally 보다는 try-with-resource를 사용하라 (0) 2021.03.28 finalizer와 cleaner 사용을 피하라 (0) 2021.03.28 다 쓴 객체 참조를 해제하라 (0) 2021.03.28 댓글