값타입은 단순하고 안전하게 다룰 수 있어야 한다.
값 타입 공유 참조
- 임베디드 타입 같은 값 타입을 여러 엔티티에서 공유하면 위험함
- 부작용(side effect) 발생 한다.
예시 ) member1과 member2 모두 city, street, 1234 의 주소를 가진다. 이때 member1의 city명만 newCity로 변경해보자
Address address = new Address("city", "street", "1234");
Member member = new Member();
member.setUsername("hello");
member.setHomeAddress(address);
em.persist(member);
Member member2 = new Member();
member2.setUsername("hello");
member2.setHomeAddress(address);
em.persist(member2);
member.getHomeAddress().setCity("newCity");
위처럼 변경 했을 시 member1과 member2가 모두 같은 것은 참조하고 있기 때문에 둘다 newCity로 변경된다.
해결 : address 객체를 복사한 후 변경해야한다.
...
Address copyAddress = new Address(address.getCity(), address.getStreet(), address.getZipcode());
Member member2 = new Member();
member2.setUsername("hello");
member2.setHomeAddress(copyAddress);
em.persist(member2);
member.getHomeAddress().setCity("newCity");
이렇게 하면 member2는 복사한 것을 참조하고 있기 때문에 member1 만 수정 할 수 있다.
값 타입 복사
- 값 타입의 실제 인스턴스인 값을 공유하는 것은 위험
- 대신 값(인스턴스)를 복사해서 사용
객체 타입의 한계
- 항상 값을 복사해서 사용하면 공유 참조로 인해 발생하는 부작용을 피할 수 있다.
- 문제는 임베디드 타입처럼 직접 정의한 값 타입은 기본 타입이 아니라 객체 타입이다.
- 자바 기본 타입에 값을 대입하면 값을 복사해서 넣지만 객체타입은 참조값을 직접 대입한다. → 이걸 막을 방법이 X
- 객체는 공유 참조는 피할 수 없다.
불변객체
- 객체 타입을 수정 할 수 없게 만들면 부작용을 원천 차단
- 값타입은 불변객체로 설계해야함
- 불변객체 : 생성시점 이후 절대 값을 변경할 수 없는 객체
- 생성자로만 값을 설정하고 Setter를 만들지 않으면 됨(setter를 없애도 되고 private로 만들어도됨)
- 참고 : Integer, String 은 자바가 제공하는 대표적 불변객체
만약에 진짜 member1의 address만 바꾸고 싶다면?
→ 객체를 새로 만들어서 그 객체를 사용해야함
// 만약 city만 바꾸고 싶다면?
Address newAddress = new Address("newCity", address.getStreet(), address.getZipCode());
// 아예 객체를 통으로 변경해줘야함
member.setHomeAddress(newAddress)
★ 결론 : 값 타입은 불변으로 만들어야 한다.
값 타입의 비교
값타입은 인스턴스가 달라도 그 안에 값이 같으면 같은 것으로 봐야한다.
- 동일성 비교 : 인스턴스의 참조 값을 비교, == 사용
- 동등성 비교 : 인스턴스의 값을 비교, equals() 사용
- 값타입은 a.equals(b)를 사용해서 동등성 비교를 해야함
- 값타입의 equals() 메소드를 적절하게 재정의(주로 모든 필드 사용)
// 자동 생성됨 -> 재정의 가능
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Address address = (Address) o;
return Objects.equals(city, address.city) &&
Objects.equals(street, address.street) &&
Objects.equals(zipcode, address.zipcode);
}
@Override
public int hashCode() {
return Objects.hash(city, street, zipcode);
}
※ equals + hashCode 사용시 참고사항
옵션을 선택하면 필드에 직접 접근하지 않고 getter에 접근을 해서 값을 가져옴
필드에 직접 접근하면 프록시일때 계산이 안된다.
'JAVA' 카테고리의 다른 글
[JAVA] 프로젝션과 페이징 (0) | 2022.01.13 |
---|---|
[JAVA] 값 타입 컬렉션 (0) | 2022.01.13 |
[JAVA] 임베디드 타입(복합값) (0) | 2022.01.11 |
[JAVA] 영속성 전이(CASCADE) + 고아객체 (0) | 2022.01.06 |
[JAVA] 즉시로딩과 지연로딩 (0) | 2022.01.05 |