ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Java mutable 과 immutable
    JAVA 2023. 10. 8. 23:06

    Mutable 과 Immutable

    Java에는 가변(mutable)객체와 불변(immutable)객체가 존재한다.
    가변 객체는 힙 영역에 생성된 객체를 변경할 수 있는 객체이고 불변 객체는 반대로 힙 영역에 생성된 객체를 변경할 수 없는 객체이다. 대표적인 불변 객체인 String 으로 예시를 보면 다음과 같다.

     

    String val1 = "abc";
    System.out.println("val1 initial " + System.identityHashCode(val1));
    
    val1 += "xyz";
    System.out.println("val1 after "+System.identityHashCode(val1));
    
    // output
    val1 initial 1435804085
    val1 after 1798286609

    위와 같이 val1이라는 String 을 선언하고 처음 "abc"로 초기화 한 후에 hascode를 출력해보고 val1에 "xyz"를 추가해준 뒤 다시 한번 hashcode를 출력해보았다. 결과로 나온 output은 두개가 서로 다른 값을 출력하고있다. String은 불변 객체이기에 새로 "abcxyz"라는 String을 생성한 것이다.

     

    Immutable Class

    불변 객체를 생성하기위한 규칙은 다음과 같다.

    1. 모든 필드를 private,final로 선언한다.
    2. class를 final로 선언한다
    3. setter method를 제공하지 않는다.
    4. 참조에 의해 변경 가능성이 있는 경우 방어적 복사를 이용하여 전달한다.
    5. 객체 생성을 위한 생성자 or 정적 팩토리를 추가한다.

    Immutable Class 만들기

    public class Immutable {
        public int val;
        public List<String> list;
    
        public Immutable(int val, List<String> list){
            this.val = val;
            this.list = list;
        }
    
        public int getVal(){
            return val;
        }
    
        public List<String> getList(){
            return list;
        }
    }

    위와같은 class 를 Immutable class로 바꾸어보자.

    Immutable immutable = new Immutable(0,new ArrayList<>());
    System.out.println(immutable.getVal());
    System.out.println(immutable.getList());
    
    immutable.val = 10;
    System.out.println(immutable.getVal());
    
    // output
    0
    []
    10

    현재는 위처럼 객체를 하나 만들고 내부 값을 변경시킬 수 있다.

    public final class Immutable {
        private final int val;
        private final List<String> list;
    
        public Immutable(int val, List<String> list){
            this.val = val;
            this.list = list;
        }
    
        public int getVal(){
            return val;
        }
    
        public List<String> getList(){
            return list;
        }
    }

    우선 내부 필드들을 private final로 수정했다. 이럴경우 위의 예시의 immutable.val 처럼 접근하여 값을 바꾸는것은 불가능해진다.

    Immutable immutable = new Immutable(0,new ArrayList<>());
    List<String> list = immutable.getList();
    System.out.println(immutable.getList());
    
    list.add("hello");
    System.out.println(immutable.getList());
    
    // output
    []
    [hello]

    하지만 위의 예시처럼 list같은경우 reference를 참조히기때문에 여전히 내부 값을 수정할 수 있다. 이를 해결하기 위해 방어적 복사를 이용하여 getList를 수정해보자.

    public List<String> getList(){
    	return new ArrayList<>(list);
    }

    getList에서 반환되는 List를 새로 생성하여 반환해 주었다.

    ArrayList<String> initList = new ArrayList<>();
    initList.add("hello");
    Immutable immutable = new Immutable(0,initList);
    
    List<String> list = immutable.getList();
    System.out.println(immutable.getList());
    
    list.add("world");
    System.out.println(immutable.getList());
    System.out.println(list);
    
    // output
    [hello]
    [hello]
    [hello, world]

    getList에서 List를 새로 생성해서 반환했기때문에 immutable.getList() 한 list에 새로 값을 넣어도 내부 값은 변경되지 않았다. 하지만 list.add("world") 처럼 받아온 list에 값은 추가가 되는데, getList를 다음과 같이 변경하면 받아온 list에서의 값 변경도 불가능하게 한다.

    public List<String> getList(){
    	return Collections.unmodifiableList(list);
    }

    'JAVA' 카테고리의 다른 글

    Spring Boot AOP를 이용해 로그인 검증하기  (0) 2024.07.29
    Spring Boot MessageConverter  (0) 2024.06.24
    Java와 일급 함수  (0) 2023.09.18
    Java Generic Compile 동작  (0) 2023.09.11
    JVM Memory Area  (0) 2023.09.08
Designed by Tistory.