일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- IoC
- 자바의정석
- 다형성
- privateapi
- 인프콘
- 스프링컨테이너
- 애너테이션
- 더티채킹
- refreshtoken
- github actions
- 비정적중첩클래스
- 변경감지
- java
- SOLID
- 지네릭스
- publicapi
- DI
- 서버사이드렌더링
- 정적중첩클래스
- 클라이언트사이드렌더링
- 스파르타코딩클럽
- 항해99
- 인수테스트
- 일급컬렉션
- 9기
- 싱글톤패턴
- Spring
- Velog
- bean
- 항해99 9기
- Today
- Total
멈재
[JAVA] 20. 동일성(identity)과 동등성(equality) 본문
어떠한 두 대상이 같은지 여부를 확인하는 과정에서 흔히 동일성 비교와 동등성 비교를 하게 된다.
다만, 경우에 따라서 메모리 내 주소값이 달라도 특정 조건을 만족하면 논리적으로 같은 객체로 다뤄야 하는 경우가 존재한다.
동일성(identity): 메모리 내 주소값이 같은지 비교한다. (==)
동등성(equality): 논리적 지위가 동등한 지 비교한다. (equals)
우선 동일성 비교(==)부터 알아보자.
int, boolean과 같은 원시 타입간에 동일성 비교는 값을 비교한다.
boolean isTrue = true;
boolean isFalse = false;
System.out.println(isTrue == isFalse); // true == false >> false
System.out.println(isTrue == true); // true == true >> true
int zero = 0;
int one = 1;
System.out.println(zero == one); // 0 == 1 >> false
System.out.println(zero == 0); // 1 == 1 >> true
참조 타입에 대해서는 주소값을 비교한다.
String jae1 = "jae";
String jae2 = "jae";
String newJae = new String("jae");
System.out.println(jae1 == jae2); // 0x100 == 0x100 >> true
System.out.println(jae1 == newJae); // 0x100 == 0x200 >> false
Person mum1 = new Person("mum");
Person mum2 = new Person("mum");
System.out.println(mum1 == mum2); // 0x101 == 0x201 >> false
동일성 비교는
원시형 타입간의 비교는 값을 비교하고,
참조 타입간의 비교는 주소값을 비교한다.
다음으로는 동등성 비교이다.
동등성 비교(equals)는 동일성 비교(==)와 달리 객체에 대해서만 사용이 가능하다.
모든 객체의 조상인 Object 클래스의 equals 메서드를 보자.
public boolean equals(Object obj) {
return (this == obj);
}
반환 형태를 보면 알 수 있듯이, 단순히 동일성 비교를 하고 있다.
즉, 해당 메서드를 재정의하지 않을 경우 equals 연산자는 == 연산자와 다르지 않게 된다.
public class EqualsEx1 {
public static void main(String[] args) {
Disney lionKing1 = new Disney("lion king");
Disney lionKing2 = new Disney("lion king");
System.out.println(lionKing1.equals(lionKing2)); // 0x100 == 0x200 >> false
}
}
class Disney {
String name;
public Disney(String name) {
this.name = name;
}
}
위 예시 코드처럼 말이다.
이름이 같은 영화는 존재해서는 안된다라는 요구사항이 존재한다면 다음과 같이 equals 메서드를 재정의 해주어야 한다.
public class EqaulsEx1 {
public static void main(String[] args) {
Disney lionKing1 = new Disney("lion king");
Disney lionKing2 = new Disney("lion king");
System.out.println(lionKing1.equals(lionKing2)); // true
}
}
class Disney {
String name;
public Disney(String name) {
this.name = name;
}
@Override
public boolean equals(Object o) {
Disney disney = (Disney) o;
return this.name.equals(disney.name);
}
}
만약 Hash 값을 사용하는 Collection(HashSet, HashMap, HashTable)을 사용할 때에는 위와 같이 해줄 경우 문제가 발생한다.
그 이유는 위 세 컬렉션은 객체가 논리적으로 같은지 비교할 때 다음과 같은 과정을 거친다.
hashCode 메서드의 반환 값이 같은 지를 우선 일치하는지 확인하고,
equals 메서드의 리턴 값이 true여야 논리적으로 같은 객체라고 판단한다.
이처럼 hashCode 메서드가 재정의 되어있지 않는다면 두 Disney 객체는 서로 다른 hashCode 메서드의 반환 값으로 인해 다른 객체로 판별되기 때문이다.
이 문제를 해결하려면 equals 메서드와 hashCode 메서드를 재정의 해주어야 한다.
public class EqualsAndHashCodeEx {
public static void main(String[] args) {
Set set = new HashSet();
Disney lionKing1 = new Disney("lion king");
Disney lionKing2 = new Disney("lion king");
set.add(lionKing1);
set.add(lionKing2);
System.out.println(set); // [Disney{name='lion king'}]
System.out.println(lionKing1.equals(lionKing2)); // true
}
}
class Disney {
String name;
public Disney(String name) {
this.name = name;
}
@Override
public boolean equals(Object o) {
Disney disney = (Disney) o;
return this.name.equals(disney.name);
}
@Override
public int hashCode() {
return Objects.hash(name);
}
@Override
public String toString() {
return "Disney{" +
"name='" + name + '\'' +
'}';
}
}
그렇다면 String의 경우에는 어떨까?
String mumjae1 = new String("mumjae");
String mumjae2 = new String("mumjae");
System.out.println(mumjae1.equals(mumjae2)); // true
신기하게도 기존 객체들과는 달리 String 간의 동등성 비교는 true를 반환한다.
이것이 가능한 이유는 String 클래스의 equals 메서드는 기본적으로 재정의가 되어있기 때문이다.
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;
}
참고
https://tecoble.techcourse.co.kr/post/2020-07-29-equals-and-hashCode/
https://brunch.co.kr/@mystoryg/132
https://steady-coding.tistory.com/534
'JAVA & Spring & JPA' 카테고리의 다른 글
[JAVA] 자바 애너테이션과 활용 예시 (Annotation) (0) | 2023.03.26 |
---|---|
[Java] 정확한 답이 필요하다면 float와 double은 피하라 (1) | 2023.03.10 |
[JAVA] 18. 상속(IS-A) VS 구성(HAS-A) (0) | 2023.01.02 |
[Spring/JPA] 14. AttributeConverter로 코드의 가독성을 높여보자 (0) | 2022.12.20 |
[JAVA] 13. JAVA의 HashSet은 어떻게 동작할까? (1) | 2022.12.19 |