Mutable과 Immutable
Mutable과 Immutable 비교
Mutable | Immutable |
생성된 이후 수정 가능 | 생성된 이후 수정 불가능 |
이미 존재하는 객체에 재할당 (값 변경) | 이미 존재하는 객체이더라도 새로운 객체를 생성하여 재할당 |
값을 변경할 수 있는 메소드 제공 | 값을 변경할 수 있는 메소드 제공하지 않음 |
Mutable class일 경우 Getter와 Setter 존재 | Immutable class일 경우 Getter와 setter 미존재 |
thread safe하지 않을 수 있음 (병렬 처리 시 값을 보장할 수 없게 됨) | thread safe (병렬처리 시 문제 없음) |
Legacy classes, Wrapper classes, String class 등 |
•
Mutable은 객체의 수정을 허용하나, Immutable인 경우 객체의 수정을 허용하지 않는다.
•
수정이 필요할 경우 Mutable 객체는 기존의 객체에 수정사항을 곧바로 반영한다.
•
Immutable 객체의 경우 기존의 객체는 그대로 두고 수정사항을 반영한 새로운 객체를 생성한다.
◦
Immutable의 대표적 예시로는 String class가 존재한다.
Mutable 속성 확인
public class Mutable {
public static void main(String[] args) {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("abc");
System.out.println(stringBuilder);
System.out.println(System.identityHashCode(stringBuilder));
stringBuilder.append("def");
System.out.println(stringBuilder);
System.out.println(System.identityHashCode(stringBuilder));
}
}
Java
복사
•
System.identityHashCode(Object) 코드를 통해 String 객체의 메모리 주소값을 함께 출력하였다. 실행 결과는 아래와 같다.
•
같은 stringBuffer에 할당되었고 stringBuilder의 값을 abc에서 abcdef로 변경하였을 때, 메모리 주소 값이 변경되지 않았다. 이는 곧 1785210046의 메모리 주소에 할당된 abc란 값이 abcdef로 변한 것임을 나타낸다. 따라서 StringBuffer class는 Mutable하게 동작한다.
Immutable 속성 확인
public class Immutable {
public static void main(String[] args) {
String str = "abc";
System.out.println(str);
System.out.println(System.identityHashCode(str));
str += "def";
System.out.println(str);
System.out.println(System.identityHashCode(str));
}
}
Java
복사
•
마찬가지로 System.identityHashCode(Object) 코드를 통해 String 객체의 메모리 주소값을 함께 출력하였다. 실행 결과는 아래와 같다.
•
같은 str 변수에 할당되었지만 str의 값을 abc에서 abcdef로 변경하였을 때, 메모리 주소 값도 같이 변경되었다.
•
이는 곧 1785210046의 메모리 주소에 할당된 abc란 값이 abcdef로 변한 것이 아니라 1151020327의 메모리 주소에 abcdef란 값을 가진 String 객체가 새로 생성된 것임을 나타낸다.
◦
String class는 Immutable하게 동작한다.
Mutable한 객체를 Immutable한 객체로 사용하기
방어적 복사(defensive copy)
•
mutable 객체는 생성 이후 값 변경이 자유롭게 가능하여 개발자가 의도하지 않은 객체의 변경이 일어날 수 있다는 단점이 존재한다.
•
단점의 주 원인은 "레퍼런스를 참조한 다른 객체에서 객체를 변경"하기 때문이다. 이를 해결하고자 객체의 변경이 필요할 경우 참조가 아닌 객체의 방어적 복사(defensive copy)를 통해 새로운 객체를 생성한 후 변경하도록 한다.
◦
적시에 방어적 복사본을 만들라 파트 참고
Setter 메소드 제거
•
mutable 객체와 immutable 객체의 가장 큰 차이는 생성된 이후 수정이 가능한가, 가능하지 않은가에 있다.
•
따라서 mutable 객체를 immutable하게 사용하려면 생성된 이후 변경이 불가능하게 만들어주면 된다.
•
class 객체 생성 이후 값을 변경하는 역할은 setter 메소드가 맡고 있으므로 이를 제거하여 변경을 막는다. 마치 '읽기 전용'으로 파일을 여는 것과 비슷하다.
그 외
•
final 키워드를 사용한다.
◦
final class는 상속이 불가하며 final 메소드는 오버라이딩이 불가하다. 원치않는 class와 메소드 내의 수정을 막을 수 있다.
•
모든 클래스 변수를 private과 final로 선언한다.
◦
setter 메소드가 없을 경우 외부에서 접근할 수 없어 변경이 불가해진다. 변수들을 private으로 선언해두고 class 내부의 메소드만으로 변수를 접근 가능하게 하는 정보 은닉(Data Hiding)을 떠올리면 이해가 쉽다.