Eqaulity 와 Identity
이 두 개념은 == 연산자와 equals() 메서드를 통해 구현되며, 그 차이를 명확히 아는 것이 중요하다고 생각됩니다.
==
연산자를 사용하여 비교합니다.public class IdentityExample {
public static void main(String[] args) {
String str1 = new String("hello");
String str2 = new String("hello");
System.out.println(str1 == str2); // false (다른 객체)
String str3 = "hello";
String str4 = "hello";
System.out.println(str3 == str4); // true (String Pool 내부에서 같은 객체 공유)
}
}
/*
str1 과 str2 는 new 키워드를 사용하여 새로운 객체를 생성했기 때문에 메모리 주소가 다릅니다.
즉, str1 == str2 는 false 가 나옵니다.
str3 과 str4 는 String Pool 에 저장된 동일한 객체를 참조하므로 str3 == str4 는 true 입니다.
*/
equals()
메서드를 사용하여 비교합니다.equals()
메서드는 기본적으로 Object 클래스에서 제공하는 메서드이며, Object의 기본 구현은 ==
과 동일하게 동작합니다.public class EqualityExample {
public static void main(String[] args) {
String str1 = new String("hello");
String str2 = new String("hello");
System.out.println(str1.equals(str2)); // true (문자열 값이 같음)
Person p1 = new Person("Alice");
Person p2 = new Person("Alice");
System.out.println(p1.equals(p2)); // false (Person 클래스에서 equals() 오버라이드하지 않으면 기본적으로 == 비교)
}
}
class Person {
String name;
public Person(String name) {
this.name = name;
}
}
/*
- str1.equals(str2) 는 true 가 나오지만 p1.equals(p2) 는 false 가 나옵니다.
- String 클래스는 equals()를 오버라이드하여 문자열 값이 같은지를 비교하지만,
Person 클래스는 equals()를 오버라이드하지 않았기 때문에
Object의 equals()를 그대로 사용하여 == 와 같은 비교를 수행하게 됩니다.
*/
equals() 메서드를 오버라이딩하면 논리적 동등성을 비교할 수 있습니다.
class Person {
String name;
public Person(String name) {
this.name = name;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Person person = (Person) obj;
return name.equals(person.name);
}
}
public class EqualsOverrideExample {
public static void main(String[] args) {
Person p1 = new Person("Alice");
Person p2 = new Person("Alice");
System.out.println(p1.equals(p2)); // true (이제 논리적으로 같은 객체로 판단됨)
}
}
반사성 R: x.equals(x) 는 항상 true 여야 합니다.
대칭성 S: x.equals(y) 가 true 이면 y.equals(x) 도 true 여야 합니다.
추이성 T: x.equals(y) 가 true 이고 y.equals(z) 가 true 이면 x.equals(z) 도 true 여야 합니다.
일관성 C: equals() 결과는 입력값이 변하지 않는 한 항상 동일해야 합니다.
Null 비교: x.equals(null) 은 항상 false 를 반환해야 합니다.
import java.util.Objects;
class Person {
String name;
public Person(String name) {
this.name = name;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Person person = (Person) obj;
return name.equals(person.name);
}
@Override
public int hashCode() {
return Objects.hash(name);
}
}
public class HashCodeExample {
public static void main(String[] args) {
Person p1 = new Person("Alice");
Person p2 = new Person("Alice");
System.out.println(p1.equals(p2)); // true
System.out.println(p1.hashCode() == p2.hashCode()); // true
}
}
컬렉션(예: HashSet, HashMap, TreeSet 등)
equals() 와 hashCode() 를 적절히 오버라이드하지 않으면, 해시 기반 컬렉션에서 중복을 제대로 처리할 수 없습니다.
객체 비교 로직
==
를 사용하면 의도치 않게 다른 객체로 판별될 수 있습니다. 따라서 값을 비교할 때는 equals() 를 사용해야 합니다.
객체 동작 예측 가능성
equals() 와 hashCode() 를 잘못 오버라이드하면 버그가 발생할 가능성이 높아집니다.
== 와 equals() 의 차이를 직관적으로 본다면 ?
메모리 주소 비교 (Identity) 값 비교 (Equality)
----------------------------------------------------
Person p1 = new Person("Alice"); | Person p1 = new Person("Alice");
Person p2 = new Person("Alice"); | Person p2 = new Person("Alice");
p1 == p2 -> false | p1.equals(p2) -> true (equals 오버라이딩)
----------------------------------------------------