lombok 라이브러리를 애용하다보면, 자주 사용하게 되는 애노테이션들이 있다. (너무 편리하게 애용하고 있다)
하지만, 그마저도 자주쓰는 애노테이션끼리 묶어 @Data라는 애노테이션이 있는데,
@Getter
, @Setter
, @ToString
, @EqualsAndHashCode
, @RequiredArgsConstructor
를 자동으로 적용해준다@Getter
와 @Setter
그리고 @RequiredArgsConstructor
의 경우에는 별도로도 자주 사용하기에, 각 애노테이션의 역할을 잘 알고 있다. @ToString
도 직관적으로 해당 클래스를 toString으로 반환할 수 있게 해주는 역할이다. @EqualsAndHashCode
은 뭔가 익숙하지 않은 애노테이션인데, 왜 굳이 @Data
안에 있을까 궁금해져서 파헤치기 시작해봤다.
equals
, hashCode
자동 생성해주는 애노테이션이다. equals
는 두 객체의 내용이 같은지, 동등성(equality) 를 비교하는 연산자hashCode
는 두 객체가 같은 객체인지, 동일성(identity) 를 비교하는 연산자equals
는 String비교연산자로 유명하고 Hashcode
는 객체 주소값을 변환한 값이라고 다들 알고 있어서 언뜻보면 맞는말 같고 그렇게 보인다. 하지만, 아래 한번 예시를 보자
@ResponseBody
@RequestMapping("/model-attribute-v1")
public String modelAttributeV1(@ModelAttribute HelloData helloData){
HelloData helloData2 = new HelloData();
helloData2.setUsername("hello");
helloData2.setAge(20);
int code1 = helloData.hashCode(); //99166983
int code2 = helloData2.hashCode(); //99166983
System.out.println(helloData.equals(helloData2)); // true
return "ok";
}
hashCode()
의 메소드를 보면 반환값이 integer값이다.분명 다른 주소값을 갖는 객체들인데, 왜 Hashcode값은 동일할까?
hashcode()메소드 내부값을 들여다보았따.
public int hashCode() {
int PRIME = true;
int result = 1;
result = result * 59 + this.getAge();
Object $username = this.getUsername();
result = result * 59 + ($username == null ? 43 : $username.hashCode());
return result;
}
}
@Data어노테이션의 hashcode()를 확인하기위해, Build를 돌려, 생성된 class파일을 찾아보았다.
여기서 알게된 놀라운 사실은 hashcode는 객체 주소값이 아닌객체 내부의 있는 값만 다룬다는 점이다.
그래서 한가지 실험을 해보았다.
만약, 동일한 필드값(내부의 값)을 가지는 객체 2개를 만들고
각각, 인스턴스를 만드는데, 내부의 값이 같다면? 어떻게 될까?
@Data
public class HelloData {
private String username;
private int age;
}
@Data
public class HelloData2 {
private String username;
private int age;
}
@ResponseBody
@RequestMapping("/model-attribute-v1")
public String modelAttributeV1(HelloData helloData){
HelloData2 helloData2 = new HelloData2();
helloData2.setUsername("hello");
helloData2.setAge(20);
int code1 = helloData.hashCode();
int code2 = helloData2.hashCode();
System.out.println(code2 == code1); // true
System.out.println(helloData2.equals(helloData)); // false
return "ok";
}
원하는대로, hashcode의 값은 서로 일치하였으나, 동일한 객체임을 확인하는 equals()
에서는 false
가 나온다는 것을 알 수 있었다.
hashcode()
이며,equals()
라는 것을 깨닫는 시간이었다.내부 필드값이 동일하다면, hashcode값도 계속해서 동일함을 알 수 있었다.
hashMap<Integer, String>과 같은 hashMap의 key값을 구분할 때,
Integer타입
임에도 내부 값이 다름으로, hashcode를 기반으로 구분하여, 좋은 성능을 낼 수 있다고 한다.
@EqualsAndHashCode
애노테이션에서 생성하는 hashcode()
equals()
은 아래와 같다.hashcode()
이다 equals()
이다.Object.equals()
도 내부적으로는 ==
연산을 취하지만, String과 같은 다른 객체 타입으로 상속되면서 equals()
가 오버라이딩되어 구현된 메소드이다.