자바에서 SerialVersionId 사용되는 예제를 보다가 @EqualsAndHashCode.include
라는 어노테이션이 붙어있길래, 해당 어노테이션이 어떤 것인지 궁금해졌다.
@EqualsAndHashCode
를 찾아보니 lombok 어노테이션으로 equals(Object other)
와 hashCode()
를 생성해주는 어노테이션이었다.
equals와 hashCode는 모든 Java 객체의 부모 객체인 Object 클래스에 정의되어 있다. 그렇기 때문에 Java의 모든 객체는 Object 클래스에 정의된 equals와 hashCode 함수를 상속받고 있다.
Object 클래스에서 구현된 equals()
는 두 객체가 동일 객체인지 확인 하는 기능이다.
public boolean equals(Object obj) {
return (this == obj);
}
오로지 참조값(객체의 주소)가 같은지를 확인한다 (동일성)
동일성 비교는 == 비교다. 객체 인스턴스의 주소 값을 비교한다.
동등성 비교는 equals() 메소드를 사용해서 객체 내부의 값을 비교한다.
참고로 primitive 타입이 == 비교를 통해 값 비교가 가능한 이유는 아래와 같다.
변수 선언부는 Java Runtime Data Area의 Stack 영역에 저장이 되고, 해당 변수에 저장된 상수는 Runtime Constant Pool에 저장되어있다. Stack의 변수 선언부는 해당 Runtime Contant Pool의 주소값을 가지게 되고, 만약 다른 변수도 같은 상수를 저장하고 있다면, 이 다른 변수도 같은 Runtime Contant Pool의 주소값을 가지게 때문에 엄밀히 말하면 primitive type 역시 주소값 비교가 되는 것이다.
하지만 자바에서는 보통 eqauls()
메소드는 두 객체의 동등성을 비교할 때 자주 사용한다. 다시 말해, 두 객체의 내부 value가 같은지를 확인하는 것으로 쓰인다. (String class의 equals)
그래서 내부 값이 같은지 비교를 원할 때는 해당 메소드를 override를 하면 된다.
String class의 eqauls 구현부를 보면 아래와 같다.
// String class
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;
}
// StringLatin1 class
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;
}
우선 같은 객체인지 비교를 하고, 문자열 인코딩 방식에 따라서 내부 equals를 호출하는데 보면 각 바이트 값이 같은지를 확인한다. 즉 모든 문자가 같은지를 확인하게 된다.
객체 해시코드란 객체를 식별하는 하나의 정수값을 말한다. Object의 hashCode()
메소드는 객체의 메모리 번지를 이용해서 해시코드를 만들어 리턴하기 때문에 객체 마다 다른 값을 가지고 있다.
public native int hashCode();
여기서 native 키워드는 메소드가 JNI(Java Native Interface)라는 native code를 이용해 구현되었음을 의미한다. native는 메소드에만 적용가능한 제어자로, C or C++ 등 Java가 아닌 언어로 구현된 부분을 JNI를 통해 Java에서 이용하고자 할 때 사용된다. 우리같은 일반 개발자는 어디에서도 사용할 수 없다.
이러한 hashCode는 HashTable과 같은 자료구조를 사용할 때 데이터가 저장되는 위치를 결정하기 위해 사용된다.