티스토리 뷰
동일성(Identity) vs. 동등성(Equality) (feat. equals(), hashCode())
ellie.strong 2022. 2. 25. 18:18로또 미션을 진행하면서 로또 번호 하나를 포장하는 클래스인 LottoNumber 클래스를 생성하게 됐다.
LottoNumber 클래스는 다음과 같이 정수 타입의 number 멤버 변수 하나만을 갖는 간단한 클래스이다.
public class LottoNumber {
private final int number;
public LottoNumber(int number) {
validateRange(number);
this.number = number;
}
}
이제 로또에서 사용하는 모든 로또 번호에 대해서는 LottoNumber 클래스를 사용하게 된다.
이때 우리는 "당첨 번호와 보너스 번호의 중복을 검사"하거나 "당첨 번호와 로또 티켓의 로또 번호가 몇개가 동일한지를 검사"하는 등 LottoNumber 객체 끼리의 값을 비교하게되는 일이 발생한다.
현 상태에서 아래 테스트를 돌려보면 실패하는 모습을 볼 수 있다.
@Test
@DisplayName("같은 숫자인 로또 번호는 동일하다")
void compareLottoNumber() {
int number = 5;
LottoNumber lottoNumber1 = new LottoNumber(number);
LottoNumber lottoNumber2 = new LottoNumber(number);
assertThat(lottoNumber1.equals(lottoNumber2)).isTrue();
}
lottoNumber1.equals(lottoNumber2)의 equals() 메서드의 구현 코드를 살펴보면 다음과 같다.
💡 IntelliJ에서 "command + b"로 이동 가능!!
public boolean equals(Object obj) {
return (this == obj);
}
보면 단순히 == 연산자를 이용해 두 객체가 같은 주소값을 가리키는 지 비교한다. 이는 우리가 의도하는 비교가 아니다.
우리는 LottoNumber 두 객체가 같은 값의 number 멤버 변수를 갖는지 비교하고 싶다.
여기서 "두 객체가 같은 주소값을 가리키는지 비교"는 "동일성 비교"를 의미하며,
"같은 값의 number 멤버 변수를 갖는지 비교"는 "동등성 비교"를 의미한다.
그렇다면 여기서 의문이 든다.
동일성 비교는 == 연산자를 이용해서 비교하면 된다지만, 그럼 동등성 비교는 어떻게 하는데??
equals() 메서드도 그냥 == 연산자를 이용해서 비교한다며.. 뭐가 다른데..? 🤔
equlas() 메서드 오버라이딩
중요한게 우리는 equals() 메서드를 오버라이딩(Overiding) 해서 우리가 원하는 방식으로 비교(동등성 비교)를 하게끔 만들 수 있다는 거다.
String 클래스의 equals() 메서드
String 클래스는 다음과 같이 equals() 메서드를 오버라이딩하여 사용한다.
== 연산자를 이용해 두 객체의 동일성을 비교하고, 동일하지 않다면 String인지 판단 후, 문자 하나하나를 모두 비교하여 모두 같다면 두 객체의 내용이 같은 것으로 보고 동등하다고 판단한다.
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String aString = (String)anObject;
if (coder() == aString.coder()) {
return isLatin1() ? StringLatin1.equals(value, aString.value)
: StringUTF16.equals(value, aString.value);
}
}
return false;
}
@HotSpotIntrinsicCandidate
public static boolean equals(byte[] value, byte[] other) {
if (value.length == other.length) {
for (int i = 0; i < value.length; i++) {
if (value[i] != other[i]) {
return false;
}
}
return true;
}
return false;
}
JDK에서 제공하는 Integer, ArrayList 등 대부분의 기본 제공 클래스에서 equals() 메서드를 오버라이딩해 동등성을 비교하는 방식으로 사용하고 있는 모습을 볼 수 있다.
LottoNumber 클래스에 equals() 메서드 오버라이딩
우리도 할 수 있다.
나는 다음과 같이 equals() 메서드를 오버라이딩해 number 멤버 변수를 비교하는 방식으로 구현하였다.
💡 IntelliJ에서 "command + n" 단축키를 이용해 자동 생성할 수 있다.
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
LottoNumber that = (LottoNumber) o;
return number == that.number;
}
@Override
public int hashCode() {
return Objects.hash(number);
}
이때 hashCode() 메서드까지 오버라이딩 하는 이유는
만약 hashCOde() 메서드를 오버라이딩 하지 않을 경우 HashMap이나 HashSet과 같이 hashCode를 활용하는 클래스에서 문제가 발생할 수 있기 때문이다.
테스트 코드 실행 시 통과하는 모습을 확일 할 수 있다.
이후 "이펙티브 자바 스터디"에서 다뤘던 equals(), hashCode() 메서드에 대해서 조금 더 자세히 학습해봐야겠다.
Ref.
https://steady-coding.tistory.com/534
'Backend > Java' 카테고리의 다른 글
Unmodifiable Collection (0) | 2022.02.28 |
---|---|
try-finally 보다는 try-with-resources를 사용하라 (0) | 2022.02.28 |
Null vs. Empty vs. Blank (feat. isEmpty, isBlank) (2) | 2022.02.18 |
부생성자에서 주생성자를 호출하라 (0) | 2022.02.17 |
Random vs. ThreadLocalRandom (2) | 2022.02.17 |