-
불변객체란?
- 불변객체란 상태값을 수정할 수 없는 객체를 의미한다.
- 즉, 객체를 처음 생성(초기화) 후에는 객체가 가지는 상태를 변경할 수 없는 것을 의미한다.
불변객체를 만드는 방법
1) 캡슐화를 통해 내부 변수 접근 불가
public class Immutable { String address = "대한민국 서울시"; String gender = "female"; String identityNumber = "9706192034623"; public String getAddress() { return address; } public String getGender() { return gender; } public String getIdentityNumber() { return identityNumber; } public void setAddress(String address) { this.address = address; } public void setGender(String gender) { this.gender = gender; } public void setIdentityNumber(String identityNumber) { this.identityNumber = identityNumber; } }
다음은 개인 정보를 가진 class 이다. 주소, 성별, 주민등록번호를 가지고 있으며 외부에서는 getter(), setter() 를 통해 데이터를 조회, 수정 할 수 있다. 여기서 문제는 setter()를 통해 변경하면 안되는 값이 변경 될 가능성이 있다는 것이다.
public class Immutable { private String address = "대한민국 서울시"; private String gender = "female"; private String identityNumber = "9706192034623"; public String getAddress() { return address; } public String getGender() { return gender; } public String getIdentityNumber() { return identityNumber; } }
1. 내부 인스턴스 변수는 getter() 메소드를 통해서만 접근 가능하도록 private으로 변경하였다.
2. setter() 메소드를 삭제하여 외부에서 값을 변경 할 수 없도록 하였다.
캡슐화를 통해 공개된 인터페이스를 통해서만 접근 할 수 있으며, Getter만 열려있는 상태이기에 수정자를 통한 멤버 수정이나, 직접 객체의 멤버에 접근해서 수정할 수 있는 방법은 막혔다.
다음과 같은 변경은 객체의 불변성을 보장할까?
캡슐화를 통해 보장된 불변성은 상속을 사용하는 순간 깨진다.
2) final을 통해 객체의 값 변경을 막자
다음은 Immutale 클래스를 상속받은 extendsImmutable 클래스이다.
public class extendsImmutable extends Immutable{ private String address; private String gender; private String identityNumber; public extendsImmutable(String address,String gender,String identityNumber){ super(); this.address = address; this.gender = gender; this.identityNumber = identityNumber; } @Override public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } @Override public String getGender() { return gender; } public void setGender(String gender) { this.gender = gender; } @Override public String getIdentityNumber() { return identityNumber; } public void setIdentityNumber(String identityNumber) { this.identityNumber = identityNumber; } @Override public String toString() { return "extendsImmutable{" + "address='" + address + '\'' + '}'; } }
1) extendsImmutable은 상속을 받으며 setter를 추가할 수 있고, extendsImmutable Instance는 thread safety를 보장받을 수 없다.
2) Immutable에서는 정상적이던 동작도 extendsImmutable가 메소드 오버라이딩을 통해 오작동하게 만들 수 있다.
- 다형성을 통해서 Immutable 객체를 제공 받는다면 이 객체가 Immutable객체인지, 상속을 받은 신뢰할수 없는 객체인지 알기 어렵다.
- final class로 선언하여 상속을 하지 못하게 함으로써 해결 가능하다.
public final class Immutable
또 다른 문제에 대하여 생각해보자. 만일, Immutable 클래스에 가족 정보를 나타내는 Family Reference Type을 멤버변수로 추가했다고 해보자. 다음과 같은 코드에서는, 외부에서 getFamily() 호출 시 객체 내부의 참조 값을 넘겨주게 된다. 그리고 객체 외부에서는 그 참조값을 통해서 객체 내부의 맴버를 변경할 수 있게 되어 객체의 불변성을 무너지게 한다.
public final class Immutable { private String address = "대한민국 서울시"; private String gender = "female"; private String identityNumber = "9706192034623"; private Family family = new Family(); public Family getFamily() { return family; }
3) 생성자나 상태 접근자를 호출 시 DeepCopy를 사용
이는, DeepCopy를 이용하여 해결 할 수 있다.
public Family getFamily() { return new Family(); }
다음과 같이 함수 호출 시마다, 새로운 객체를 생성, 반환하여 외부로부터의 접근을 방지한다.
https://mangkyu.tistory.com/131