-
Java 불변 객체 - Immutable ObjectJava & Kotlin 2021. 4. 24. 13:57반응형
1. 서론
자바의 객체를 불변 객체로서 보호하는 방법입니다.
2. 불변 객체란 (Immutable Object)
객체가 불변하다는 것은 생성된 이후에 변하지 않는 것을 의미합니다.
아마도 자바 언어를 처음 배울 때 쓰레드 공유를 통해서 은행에 잔고를 확인하는 코드는 익숙하실 것 같습니다. 쓰레드 동기화 기능을 사용하지 않으면, 동시에 여러 곳에서 잔고를 빼가고 잔액이 마이너스를 기록하는 것을 볼 수 있는데요.
불변 객체도 비슷합니다. 결국에는 여러 곳에서 참조를 많이 하는, 동시적으로 이벤트가 발생하는 애플리케이션에서 변하지 않는 값을 가질 때 사용될 수 있습니다.
하지만 당연히 불변 객체의 단점도 있겠죠? 생성할 때 초기값이 아닌, 새로운 값을 입력하려면 새 객체를 만들어야 합니다. 그만큼 자원의 소모가 많아질 수밖에 없습니다. 그만큼 코드 재사용성이 떨어진다고 말할 수 있습니다.
물론 메모리에서 해제가 되는 시점은 가변 객체와 마찬가지로, 더 이상 참조하지 않을 때입니다.
이러한 단점에도 불구하고, 오라클 문서에서는 그만큼 같은 객체를 잘 사용하게 된다면 GC의 overhead를 막을 수 있다고 합니다. 당연하게 한번 메모리에 할당 후 같은 객체를 계속해서 호출하더라도, 새롭게 할당할 이유가 없기 때문입니다.
또한 가변 객체의 값이 의도치 않게 변하는 것을 막는 불필요한 코드도 제거할 수 있다고 합니다.
3. 불변 객체를 정의하는 방법
오라클 문서에서는 불변 객체를 정의하는 4가지 전략을 소개하고 있습니다.
- "setter" 메서드를 사용하지 마라
- 모든 필드(field)를 final, private으로 사용하라
- 상속을 받는 subclass가 해당 메소드를 오버 라이딩하게 하지 마라. 간단한 방법은 그냥 final 클래스로 선언하라
- 만약 인스턴스 필드가 가변 객체에 포함되어 참조되고 있다면, 해당 객체가 변하게 허락하지 마라 (가변 객체에 제공하지 마라)
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/
반응형'Java & Kotlin' 카테고리의 다른 글
[Lombok]@Builder과 생성자 애노테이션 (0) 2021.08.08 Lambda(람다) 표현식 적용하기 (0) 2021.06.28 (Java)for문 / Collection.size() 성능 개선 (0) 2021.04.04 (java)String vs StringBuilder 수행 시간 차이 (4) 2021.03.28 (Java)파사드 디자인 패턴 - Facade Design Pattern (0) 2021.03.28