JAVA

[JAVA] 값 타입과 불변객체

응디 2022. 1. 11. 17:56

값타입은 단순하고 안전하게 다룰 수 있어야 한다.

 

값 타입 공유 참조

  • 임베디드 타입 같은 값 타입을 여러 엔티티에서 공유하면 위험함
  • 부작용(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