ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Java 불변 객체 - Immutable Object
    Java & Kotlin 2021. 4. 24. 13:57
    반응형

    1. 서론

    자바의 객체를 불변 객체로서 보호하는 방법입니다.

     

    2. 불변 객체란 (Immutable Object)

    객체가 불변하다는 것은 생성된 이후에 변하지 않는 것을 의미합니다.

     

    아마도 자바 언어를 처음 배울 때 쓰레드 공유를 통해서 은행에 잔고를 확인하는 코드는 익숙하실 것 같습니다. 쓰레드 동기화 기능을 사용하지 않으면, 동시에 여러 곳에서 잔고를 빼가고 잔액이 마이너스를 기록하는 것을 볼 수 있는데요.

     

    불변 객체도 비슷합니다. 결국에는 여러 곳에서 참조를 많이 하는, 동시적으로 이벤트가 발생하는 애플리케이션에서 변하지 않는 값을 가질 때 사용될 수 있습니다.

     

    하지만 당연히 불변 객체의 단점도 있겠죠? 생성할 때 초기값이 아닌, 새로운 값을 입력하려면 새 객체를 만들어야 합니다. 그만큼 자원의 소모가 많아질 수밖에 없습니다. 그만큼 코드 재사용성이 떨어진다고 말할 수 있습니다.

     

    물론 메모리에서 해제가 되는 시점은 가변 객체와 마찬가지로, 더 이상 참조하지 않을 때입니다. 

     

    이러한 단점에도 불구하고, 오라클 문서에서는 그만큼 같은 객체를 잘 사용하게 된다면 GC의 overhead를 막을 수 있다고 합니다. 당연하게 한번 메모리에 할당 후 같은 객체를 계속해서 호출하더라도, 새롭게 할당할 이유가 없기 때문입니다. 

     

    또한 가변 객체의 값이 의도치 않게 변하는 것을 막는 불필요한 코드도 제거할 수 있다고 합니다. 

     

    3. 불변 객체를 정의하는 방법

    오라클 문서에서는 불변 객체를 정의하는 4가지 전략을 소개하고 있습니다.

     

    1. "setter" 메서드를 사용하지 마라
    2. 모든 필드(field)를 final, private으로 사용하라
    3. 상속을 받는 subclass가 해당 메소드를 오버 라이딩하게 하지 마라. 간단한 방법은 그냥 final 클래스로 선언하라
    4. 만약 인스턴스 필드가 가변 객체에 포함되어 참조되고 있다면, 해당 객체가 변하게 허락하지 마라 (가변 객체에 제공하지 마라)

     

    4. 예제

    예제 코드는 아래 사이트를 참고했습니다.

    howtodoinjava.com/java/basics/how-to-make-a-java-class-immutable/

     

     

    test.java

    필드들을 정의한 클래스입니다.

    public final class Test {
            /*
             * String은 불변 객체 입니다.
             * Integer 같은 Wrapper 클래스 또한 불변 객체 입니다.
             * Setter가 있더라도 값은 변하지 않습니다.
             */
            private final Integer immutableField1; // 불변 필드 1
            private final String immutableField2; // 불변 필드 2
            private final Date mutableField; // 가변 필드
    
            private Test(Integer fld1, String fld2, Date date)
            {
                this.immutableField1 = fld1;
                this.immutableField2 = fld2;
                this.mutableField = new Date(date.getTime());
            }
    
            public static Test createNewInstance(Integer fld1, String fld2, Date date)
            {
                return new Test(fld1, fld2, date);
            }
    
            public Integer getImmutableField1() {
                return immutableField1;
            }
    
            public String getImmutableField2() {
                return immutableField2;
            }
    
            /*
             * Date는 가변 객체이므로 값이 변합니다.
             * 그래서 return 값으로 새 Date 객체를 만들어 현재의 시간을 가지고 옵니다.
             */
            public Date getMutableField() {
                return new Date(mutableField.getTime());
            }
    
            @Override
            public String toString() {
                return immutableField1 +" - "+ immutableField2 +" - "+ mutableField;
            }
    }

    TestMain.java
    Test 객체를 사용하는 클래스입니다.

    public class TestMain {
        public static void main(String[] args) {
            /*
             * 현재 생성자 초기화에 사용되는 필드들은 불변 필드
             */
            Test im = Test.createNewInstance(100,"Test", new Date());
            System.out.println("====================== 첫번째 초기화 ======================");
            System.out.println(im);
    
            /*
             * 두번째 초기화
             * 10000, "Test changed", 10 입력
             */
            tryModification(im.getImmutableField1(),im.getImmutableField2(),im.getMutableField());
            System.out.println("====================== 두번째 초기화 ======================");
            System.out.println(im);
        }
    
        private static void tryModification(Integer immutableField1, String immutableField2, Date mutableField) {
            immutableField1 = 10000;
            immutableField2 = "Test changed";
            mutableField.setDate(10);
        }
    }

    결과

    ====================== 첫번째 초기화 ======================
    100 - Test - Sat Apr 24 13:35:14 KST 2021
    
    ====================== 두번째 초기화 ======================
    100 - Test - Sat Apr 24 13:35:14 KST 2021

     

    첫 번째, 두 번째의 결과가 같습니다. 두번째 초기화에서는 다른 값을 입력했는데 불구하고요. 

     

    말 그대로 '불변(不辨)' 했습니다.

     

    코딩을 하다 보면 같은 객체를 실수로 다시 호출해서 값이 변하게 되는 경우가 많습니다. 이때 동기화 문제가 없이, thread-safe 한 코드를 작성할 수 있습니다. 

     

    5. 참고 문헌

    docs.oracle.com/javase/tutorial/essential/concurrency/immutable.html

    howtodoinjava.com/java/basics/how-to-make-a-java-class-immutable/

    반응형

    댓글

Designed by Tistory.