2023. 5. 27. 20:36ㆍJAVA
불변 객체란 뭘까??
불변 객체란 생성 후 그 상태를 바꿀 수 없는 객체를 말한다.
따라서, multi - thread 환경에서 안전하다!!
그렇다면, 아래의 코드는 불변 객체일까?? 아닐까?
public class Phone {
private final String madeBy;
private final Screen screen;
private int useAge;
public Phone(String madeBy, Screen screen, int useAge) {
this.madeBy = madeBy;
this.screen = screen;
this.useAge = useAge;
}
private final String madeBy;
private final Screen screen;
private int useAge;
public Phone(String madeBy, Screen screen, int useAge) {
this.madeBy = madeBy;
this.screen = screen;
this.useAge = useAge;
}
getter, setter ...
}
package immutableObject;
public class Screen {
private int horizontalPixels;
private int verticalPixels;
public Screen (int horizontalPixels, int verticalPixels){
this.horizontalPixels = horizontalPixels;
this.verticalPixels = verticalPixels;
}
getter, setter ...
}
fianl 키워드를 사용했기 때문에 불변 객체라고 생각할 수 있지만, 아직까지는 불변객체가 아니다!
아래와 같이 getScreen() 메서드를 사용해서 screen 객체의 값을 변경할 수 있다.
public class PhoneTest {
public static void main(String args[]){
Phone phone = new Phone("samsung",new Screen(400,300),1);
// phone.getMadeBy() = "apple"; -> 컴파일 에러
phone.getScreen().setHorizontalPixels(1000);
// phone.getUseAge() = 2; -> 컴파일 에러
}
}
Phone 객체의 madeBy 필드와 useAge 같은 경우에는 단순히 final과 setter를 생성하지 않는 것만으로도 불변의 형태를 완성시킬 수 있다.
하지만, screen과 같은 참조 타입은 추가적인 작업이 필요하다.
추가적인 작업이란 Screen 또한 불변 객체로 만드는 것이다.
public class Screen {
private final int horizontalPixels;
private final int verticalPixels;
public Screen (int horizontalPixels, int verticalPixels){
this.horizontalPixels = horizontalPixels;
this.verticalPixels = verticalPixels;
}
이렇게 하면, Phone은 불변객체가 될 수 있다!
하지만, 참조변수가 일반 객체가 아니라 배열일 경우에는 위와 같은 방식대로 하면, 불변객체가 될까?
public class Phones {
private final List<Phone> phones;
public Phones(List<Phone> phones) {
this.phones = phones;
}
public List<Phone> getPhones() {
return phones;
}
}
public static void main(String args[]){
List<Phone> phones = new ArrayList<>();
phones.add(new Phone("samsung",new Screen(800,600),1));
phones.add(new Phone("apple",new Screen(1000,800),2));
Phones phones1 = new Phones(phones);
for(Phone phone : phones1.getPhones()) {
System.out.println(phone.toString());
}
phones.add(new Phone("Lg",new Screen(600,500),3));
for(Phone phone : phones1.getPhones()) {
System.out.println(phone.toString());
}
}
아래와 같은 방식으로 Phones 타입의 객체를 만든다면, Phones 타입의 객체의 불변성은 깨지게 된다.
=> 현재 외부에서 phone 객체를 만들어 phones의 인스턴스 변수에 추가하고 있다.
어떻게 하면, Phones 타입의 객체의 불변성을 지킬 수 있을까??
현재, Phones타입의 생성자를 통해 외부에서 phone 객체를 만들어 추가하고 있다. 이를 방지하려면 생성자에 추가적인 조작이 필요해보인다.
답은 생성자를 통해 값을 전달받을 때 new ArrayList<>(cars)를 통해 새로운 값을 참조하도록 하는 것이다.
이렇게 되면, 외부에서 넘겨주는 cars와 내부의 cars가 다르기 때문에 외부에서 제어가 불가능합니다.
public class Phones {
private final List<Phone> phones;
public Phones(List<Phone> phones) {
this.phones = new ArrayList<>(phones);
}
public List<Phone> getPhones() {
return phones;
}
}
하지만, 이렇게 해도 getter()를 사용해 여전히 외부에서 조작이 가능합니다.
public class PhoneTest {
public static void main(String args[]){
List<Phone> phones = new ArrayList<>();
phones.add(new Phone("samsung",new Screen(800,600),1));
phones.add(new Phone("apple",new Screen(1000,800),2));
Phones phones1 = new Phones(phones);
phones1.getPhones().add(new Phone("Lg",new Screen(600,600),3));
for(Phone phone : phones1.getPhones()) {
System.out.println(phone.toString());
}
}
}
이는 Collections이 제공하는 unmodifieableList() 메서드를 활용해서 해결할 수 있습니다.
public class Phones {
private final List<Phone> phones;
public Phones(List<Phone> phones) {
this.phones = new ArrayList<>(phones);
}
public List<Phone> getPhones() {
return Collections.unmodifiableList(phones);
}
}
public class PhoneTest {
public static void main(String args[]){
List<Phone> phones = new ArrayList<>();
phones.add(new Phone("samsung",new Screen(800,600),1));
phones.add(new Phone("apple",new Screen(1000,800),2));
Phones phones1 = new Phones(phones);
phones1.getPhones().add(new Phone("Lg",new Screen(600,600),3));
for(Phone phone : phones1.getPhones()) {
System.out.println(phone.toString());
}
}
}
최종적으로, 불변 객체로 만들려고 하는 객체의 인스턴스 변수가 배열일 경우에는 생성자와 get() 메서드에 추가적인 작업이 필요합니다.
지금까지 불변객체란 무엇인가, 그리고 불변객체를 만드는 방법에 대해서 알아봤습니다.
모두 열공하세요~
'JAVA' 카테고리의 다른 글
IntelliJ의 Run 버튼의 의미 - Build(빌드)와 Compile(컴파일) (0) | 2023.08.20 |
---|---|
ArrayList는 어떻게 용량을 관리할까? (0) | 2023.07.24 |
String vs StringBuilder vs StringBuffer (0) | 2023.05.27 |
final에 대하여 (4) | 2023.05.26 |
정적 팩토리 메서드 (1) | 2023.05.23 |