기본 자료형을 비교할 때 우리는 ==
비교 연산자를 통해서 비교를 한다.
참조 자료형을 ==
비교 연산자를 통해 비교를 한다면 객체의 참조값을 비교하기 때문에 객체 내부의 값이 "자바"로 같더라도 올바른 비교를 할 수 없다.
그렇다면 어떻게 비교를 할 수 있을까?
Object클래스의 equasl()
와 hashCode()
를 이용하면 된다.
Object 클래스는 모든 클래스가 기본적으로 상속받는 클래스이다. 이 클래스안에는 객체가 가져야할 최소한의 메소드를 정의해 놓았다. 그 중에 equals()
메소드는 Obejct 클래스의 hashCode()
메소드 리턴값을 가지고 비교를 한다. 그래서 우리는 올바른 비교를 위해 equals()
메소드를 오버라이딩 시켜야 한다.
import java.util.Objects;
public class Student {
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public boolean equals(Object o) {
if (this == o) return true; //같은 객체를 참조하여 참조값이 같은경우 true를 바로 리턴해준다.
if (o == null || getClass() != o.getClass()) return false; //비교하는 객체가 null인지 클래스가 같은지 체크한다.
Student student = (Student) o;
return age == student.age &&
Objects.equals(name, student.name); //객체 내부의 값들을 비교하여 리턴한다.
}
}
equals()
메소드는 이와같이 값이 같음을 비교할 수 있는 동등성
을 비교할 수 있다. 여기서 동등성
이란 서로 다른 객체이지만 같은 값을 가진다는 것을 의미한다.
hashCode() 메소드는 객체의 고유값을 나타낸다. 이처럼 값은 같으나 객체의 고유값이 다르기 때문에 완전히 같은 객체라고 보기는 힘들다.
그래서 우리는 객체가 내부의 값이 같다면 같은 객체임을 나타내긴 위해서 값이 같을경우 동일한 hashCode()
메소드의 값을 가져야한다. 같은 값이면 같은 객체라고 할 수 있을 때 우리는 동일성
비교를 할 수 있다.
Student 클래스에 hashCode()
를 오버라이드 하였다. 출력 결과는?
같은 hashCode()
값을 가진다. 비로소 두 객체가 동일하다고 볼 수 있는 것이다.
두 객체를 equals()
메소드를 사용하여 비교한 경우 true이면 hashCode()
가 true여야 한다.
하지만, equals()
메소드가 false를 리턴했다고 하여 hashCode()
값이 같을 수 도 있다는 말이다. 이것은 hashTable
의 특징이며 이 경우 서로 다른 hashCode()
를 가지면 hashTable
의 성능을 향상시키는 데 도움이 된다.
hashcode()는 객체의 동일성을 비교하는 메소드가 아닙니다(정확히 얘기해서는 비교하지 못할 수 있습니다.).
객체의 동일성을 비교하는 연산자는 "==" 입니다.
Object의 hashcode() 구현체를 보고 헷갈릴 수 있는데요.
Object hashcode() 구현체가 동일성을 비교하는 이유는 Object의 equals() 구현체가 "=="으로 비교하기 때문입니다.
그러면 왜 hashcode()는 객체의 동일성을 비교하지 않을 수 있을까요? 오버라이딩 하면서 2개의 메소드는 여러 규약을 준용해야합니다. 이 때문에 동일성을 비교하지 않을 수 있습니다.
Object의 equals(), hashcode()는 오버라이딩을 자주하는데요.
오버라이딩할 때 각각 규약이 존재합니다. 이때 equals가 동일성 검사를 하지 않고 동등성 검사를 하면 hashcode() 또한 동등성 검사만 진행해야합니다
(솔직히 객체의equals()의 주 목적이 동등성 검사니까 hashcode()는 거의 동등성으로 활용됩니다.)
본문에서 괴리를 느끼실 수 있습니다.
sout(s1 == s2) 하면 false 나올 것 같은데요. 두 객체가 동일하지 않기 때문에 그렇습니다.
s1과 s2가 다른 힙을 참조합니다.
참고로 이펙티브 자바에서 상세하게 설명합니다.