ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Mutable & Immutable 객체
    Java/이론&문법 2023. 9. 15. 21:45

    Mutable Object

    • 생성된 이후에 수정이 가능하며, 이미 존재하는 객체에 재할당이 가능 
    • 값을 변경할 수 있는 메소드 제공
    • Heap 영역에 생성된 객체를 변경할 수 있다. 
    • List, ArrayList, HashMap, StringBuilder, StringBuffer...

     

    Immuatable Object

    • String, Boolean, Integer, Float, Long 등
    • 외부에서 객체의 데이터를 변경할 수 없다(heap 영역에서 바꿀 수 없다.)
    • 새 객체를 만들어 reference 값을 주는 재할당만 가능 
    • 신뢰도 보장, 멀티 스레드 환경에서 안전함. 
    • 대신 객체의 값이 할당될 때마다 새로운 객체 필요(메모리 많이 잡아먹음)

     

    불변객체로 만드는 방법 

    • Integer, String 등의 API : final 선언을 통해 변경 불가능하게 만들어져 있다.
    • setter와 같이 내부 요소를 변경할 수 있는 메소드 구현하지 말 것 
    •  멤버 변수를 private + final로, class 상속받지 못하도록 class를 final로 선언
      • 단, final이 객체에 붙으면 객체의 속성까지는 관여하지 않는다. 이 부분을 더 방어해야 한다. 
    • 생성자를 private로 선언
    • 가변 객체 타입의 field 변수가 있을 경우,  방어적 복사본 전략을 사용
      • 방어적 복사란?
        • 내부의 객체를 반환할 때 객체의 복사본을 만들어 반환하는 것
        • 복사한 객체를 외부에서 바꿔도 원본 내부 객체가 변경되지 않음
        • 하지만 이 친구는 깊은 복사가 아니다. 
        • 즉 컬렉션같이 요소가 내부에 또 있는 경우, 각 컬렉션의 주소는 다르지만 내부 요소들은 동일하여 복사본에서 내부 요소를 바꾸면 원본에도 영향을 미친다. 
      • 컬렉션 타입의 필드가 있다면
        • ex) private final List<ImmutableSomething> list 라고 해두고 getter만 열어두었다 가정.
          • getter : return list; 라 하자. 
          • 외부에서 list.getList().add(new ImmutableSomething()); 하면 데이터가 추가되어 있다.
        • 컬렉션에서도 add, remove, set등을 막아야 한다. 
          • 생성자에 넣는 컬렉션,  얘를 수정해도 원본에 영향 주지 않아야 함. (완전히 참조 끊어지도록 깊은복사)
          • 직접 getter 이용해서 리스트를 꺼내더라도, 꺼낸 리스트를 수정할 수 없어야 한다. 
        • getList() 할 때 return List.copyOf(list) 
        • 생성자로 생성할 때도 깊은 복사 활용 

     

    public final class Main {
    
        private final int id;
        private final List<Integer> list;
        private final Coffee coffee;
    
        private Coffee getCoffee() {
            return new Coffee(this.coffee.price, this.coffee.name);
        }
        private int getId() {
            return id;
        }
    
        private List<Integer> getList() {
            return List.copyOf(list);
        }
    
    
        public Main(int id, ArrayList<Integer> list, Coffee coffee) {
            this.id = id;
            // this.list = list; 이렇게 해두면 list 바꿨을 때 Main의 list도 바뀜
            // 깊은 복사 수행 
            List<Integer> temp = new ArrayList<>();
            Iterator<Integer> iter = list.iterator();
            while (iter.hasNext()) {
                temp.add(iter.next());
            }
    
            this.list = Collections.unmodifiableList(temp);
    
            this.coffee = new Coffee(coffee.price, coffee.name);
        }
    
        public static void main(String[] args) {
            int id = 100;
            ArrayList<Integer> list = new ArrayList<>();
            list.add(1);
            list.add(2);
    
            Coffee coffee = new Coffee(10000, "Compose");
    
            Main main = new Main(id, list, coffee);
            // main.id = 20; 컴파일 에러
            list.add(3);
            list.set(2, 100);
            coffee = new Coffee(20000, "Starbucks");
            System.out.println(main.list); // 영향을 주지 않음 (완전히 참조 끊어짐)
            System.out.println(main.coffee.name + " " +main.coffee.price); // 그대로 만원 컴포즈
    
            System.out.println("----------------");
            // main.getList().add(3);
            // main.getList().set(1, 3);
            // main.list.add(3);
            // main.list.set(1, 3);
            // unsupportedOperation exception로 막혀있음
            
            // coffee.price = 2000; 재할당 안됨.
            // coffee.name = "MegaCoffee" 재할당 안됨. 
    
            System.out.println(main.list);
        }
    
        private final static class Coffee {
            private final int price;
            private final String name;
    
            public Coffee(int price, String name) {
                this.price = price;
                this.name = name;
            }
    
            public int getPrice() {
                return price;
            }
    
            public String getName() {
                return name;
            }
        }
    }

    출력결과 

    [1, 2]
    Compose 10000
    ----------------
    [1, 2]

     


    • ImmutableCollections의 copyOf 사용하기 (Java 9부터) 
      • add, remove뿐 아니라 setter까지 막는다. 
    static <E> List<E> copyOf(Collection<? extends E> coll) {
        return ImmutableCollections.listCopy(coll);
    }

     

    'Java > 이론&문법' 카테고리의 다른 글

    자바 Generic  (0) 2023.09.22
    java.lang package  (0) 2023.09.22
    Annotation  (0) 2023.09.15
    Nested Class  (0) 2023.09.15
    Java의 Exception  (0) 2023.09.15
Designed by Tistory.