ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 불변 객체(Immnutable Object)란??
    스터디/JAVA 2023. 5. 27. 20:36
    728x90

     

    불변 객체란 뭘까??

     

    불변 객체란 생성 후 그 상태를 바꿀 수 없는 객체를 말한다.

    따라서, 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
Designed by Tistory.