-
불변 객체(Immnutable Object)란??스터디/JAVA 2023. 5. 27. 20:36728x90
불변 객체란 뭘까??
불변 객체란 생성 후 그 상태를 바꿀 수 없는 객체를 말한다.
따라서, 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() 메서드에 추가적인 작업이 필요합니다.
지금까지 불변객체란 무엇인가, 그리고 불변객체를 만드는 방법에 대해서 알아봤습니다.
모두 열공하세요~
728x90'스터디 > JAVA' 카테고리의 다른 글
ThreadLocalRandom을 왜 사용해야할까? (1) 2023.11.01 Comparable과 Comparator는 언제 써야할까? (3) 2023.06.01 String vs StringBuilder vs StringBuffer (0) 2023.05.27 정적 팩토리 메서드 언제 사용해야할까? (1) 2023.05.23 enum에 대해서 알아보자 (java.lang.Enum 클래스 상속) (2) 2023.03.01