안녕하세요
오늘은 Java의 equals() 메소드에 대해 알아보겠습니다.
equals()는 내용이 같은지를 비교하는 것이고 ==은 같은 객체인지 확인하는 것이라는 말을 많이 들었을 것입니다.
정말 그럴까요?
우선 Java에서 ==은 같은 객체인지 아닌지를 비교하는 연산자입니다. (primitive 타입의 경우 값 비교)
우선 Object 클래스의 equals() 메소드를 한 번 살펴봅시다.
Object 클래스의 equals() 메소드는 ==으로 비교하는 것을 볼 수 있습니다.
즉, Object 클래스는 ==으로 비교하는 것과 equals() 메소드로 비교하는 것이 똑같다는 것입니다.
정말 그런지 한 번 확인해볼까요?
public class ObjectTest {
public static void main(String[] args) {
Object o1 = new Object();
Object o2 = o1;
Object o3 = new Object();
Object o4 = new Object();
System.out.println((o1 == o2) + " " + o1.equals(o2));
System.out.println((o1 == o3) + " " + o1.equals(o3));
System.out.println((o1 == o4) + " " + o1.equals(o4));
System.out.println((o2 == o3) + " " + o2.equals(o3));
System.out.println((o2 == o4) + " " + o2.equals(o4));
System.out.println((o3 == o4) + " " + o3.equals(o4));
}
}
o1과 o2는 같은 객체입니다.
이 코드를 실행해보면
==으로 비교한 것과 equals() 메소드로 비교한 결과가 모두 같습니다.
또한 같은 객체인 o1과 o2만 true가 나온 것을 볼 수 있습니다.
그럼 이제 제가 클래스를 하나 만들어보겠습니다.
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
이름과 나이를 상태로 가지는 Person이라는 클래스를 만들었습니다.
public class EqualsTest {
public static void main(String[] args) {
Person p1 = new Person("김땡땡", 20);
Person p2 = new Person("김땡땡", 20);
Person p3 = p2;
Person p4 = new Person("이땡땡", 25);
System.out.println(p1.equals(p2));
System.out.println(p1.equals(p3));
System.out.println(p1.equals(p4));
System.out.println(p2.equals(p3));
System.out.println(p2.equals(p4));
System.out.println(p3.equals(p4));
}
}
p1과 p2는 이름과 나이가 모두 같고, p2와 p3는 같은 객체입니다.
p4는 p1, p2, p3과 다른 객체이고 내용도 다릅니다.
그리고 이 코드를 실행하면 어떤 결과가 나올까요?
.
.
.
.
.
.
.
.
.
.
.
.
.
.
결과는 이렇습니다.
같은 객체인 p2와 p3의 비교에서만 true를 리턴했습니다.
p1과 p2는 나이와 이름이 모두 같은데 왜 equals()로 비교를 했을 때 다르다고 나왔을까요?
사실 이는 당연한 결과입니다. Object 클래스의 equals()는 ==으로 비교한 것과 똑같은 것을 확인했었죠?
제가 만든 Person 클래스는 Object 클래스를 상속받기 때문에 Object의 equals()와 동일합니다.
따라서 Person 객체들을 ==으로 비교한 것과 결과가 똑같이 같은 객체인 경우에만 true를 반환했던 것입니다.
그런데 저는 같은 객체일 때가 아니라 이름과 나이가 모두 동일할 때 true를 반환하고 싶습니다.
그러면 어떻게 해야 할까요?
이름과 나이가 같으면 true를 반환하도록 equals() 메소드를 오버라이딩(재정의) 해주면 됩니다.
@Override
public boolean equals(Object o) {
if (o instanceof Person) {
Person p = (Person) o;
if (p.getName() != null && p.getName().equals(this.name) && p.getAge() == this.age) {
return true;
} else {
return false;
}
} else {
return false;
}
}
저는 이렇게 작성해보았습니다.
equals() 메소드를 오버라이딩 했으니 이제 아까 코드를 다시 실행해볼까요?
public class EqualsTest {
public static void main(String[] args) {
Person p1 = new Person("김땡땡", 20);
Person p2 = new Person("김땡땡", 20);
Person p3 = p2;
Person p4 = new Person("이땡땡", 25);
System.out.println(p1.equals(p2));
System.out.println(p1.equals(p3));
System.out.println(p1.equals(p4));
System.out.println(p2.equals(p3));
System.out.println(p2.equals(p4));
System.out.println(p3.equals(p4));
}
}
그런데 equals() 메소드를 오버라이딩할 때 주의할 점이 있습니다.
다른 객체가 이것과 같은지 아닌지를 나타냅니다.
equals() 메소드는 null이 아닌 참조 객체에 대해 비교를 수행합니다.
재귀적이다. null이 아닌 참조 타입 객체 x가 있을 때 x.equals(x)는 true를 반환해야 한다.
대칭적이다. null이 아닌 참조 타입 객체 x. y가 있을 때 y.equals(x)가 true일 때만 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를 반환하거나 일관적으로 계속 false를 반환해야 한다.
null이 아닌 참조 타입 객체 x가 있을 때, x.equals(null)은 false를 리턴해야 한다.
(의역이 있을 수 있습니다.)
equals() 메소드를 오버라이딩 할 때는 위 규칙들을 반드시 지키며 재정의해야 합니다.
무슨 말인지 아직 와 닿지 않으시죠? 한 번 예를 들어보겠습니다.
아까 제가 구현한 Person 클래스를 key로 가지는 HashMap을 만들어보겠습니다.
import java.util.HashMap;
import java.util.Map;
public class HashMapTest {
public static void main(String[] args) {
Map<Person, Integer> map = new HashMap<>();
Person p1 = new Person("김땡땡", 20);
Person p2 = new Person("김땡땡", 20);
map.put(p1, 1);
map.put(p2, 2);
System.out.println(map.size());
}
}
이름과 나이가 같은 객체 p1과 p2를 만들고 map에 한 번은 p1을 key로, 한 번은 p2를 key로 엔트리를 추가했습니다.
같은 객체는 같은 해시코드를 가져야 한다는 hashCode() 메소드의 일반적인 규칙을 유지하기 위해서 equals() 메소드를 오버라이딩 했을 때는 일반적으로 hashCode()를 반드시 오버라이딩해야 한다.
@Override
public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result + age;
return result;
}
import java.util.HashMap;
import java.util.Map;
public class HashMapTest {
public static void main(String[] args) {
Map<Person, Integer> map = new HashMap<>();
Person p1 = new Person("김땡땡", 20);
Person p2 = new Person("김땡땡", 20);
map.put(p1, 1);
map.put(p2, 2);
System.out.println(map.size());
}
}
이제 1을 출력하는 것을 볼 수 있습니다.
모든 클래스가 가지고 있는 equals() 클래스와 주의점에 대해서 알아보았습니다.
읽어주셔서 감사합니다.