Java 문법 (15) - 불변 객체
불변 객체
불변 객체란?
불변 객체는 객체의 상태(내부 멤버변수)가 변하지 않는 객체를 말한다. 코드적으로 쉽게 생각하면 모든 멤버변수가 final로 선언되어 있는 객체이다. 따라서 읽기전용 getter 메서드를 제공하고, 내부 상태를 변경할 수 있는 setter는 제공하지 않는다.
불변 객체를 사용하는 이유
자바에서 객체는 참조를 통해 접근한다. 하나의 객체를 여러 변수가 공유해서 참조할 때, 변수 하나의 값만 변경하고 싶은데 모두 다 변경되어 버리는 일이 발생한다. 이를 원치 않을 때에는 불변 객체를 사용해서 실수를 막아야 한다.
예를들어 A와 B가 같이 서울에 살고, Address 객체를 공유 참조한다. 이때 B가 포항으로 이사를 가야한다면, B의 주소만 바꿔줘야 한다. 하지만 Address 객체의 값을 바꾸는 순간 A도 같은 객체를 잠조하기 때문에 A의 주소도 포항으로 바뀌게 된다.
이를 해결하기 위해서는 당연히 Address 객체를 새로 만들어 포항이라는 값을 넣어주어야 하는데, 불변 객체를 사용하지 않고서도 가능한 방법이다. 하지만 코드가 복잡해지고, 여러 사람이 함께 작업하다 보면 이러한 실수는 충분히 발생할 수 있다.
따라서 이런 경우 불변 객체를 사용하는 것이 효과적이다. 불변 객체는 멤버변수를 변경할 수 있는 setter 메서드를 제공하지 않는다. 대신에 with멤버변수
형태의 메서드를 제공하는데, 이는 해당 멤버변수의 변경을 적용시켜 새로운 객체를 반환해주는 메서드이다.
정리
불변 객체 특징
- 내부 상태 변경 불가: 생성 후 내부 상태 변경이 불가능
- with 메서드 제공: setter 메서드가 없고, 변경을 적용시킨 새로운 객체를 반환해 주는 메서드를 제공
- 적절한 제약을 추가한 것으로, 참조로 인한 실수를 줄일 수 있음
- 내부 상태가 변경되지 않기 때문에 객체를 공유할 수 있음. 메모리 사용량 줄임
- 변경이 잦은 경우에는 객체를 계속 생성해야되기 때문에 메모리 낭비될 수 있음
- String, Integer 등이 대표적인 불변 객체
예제 코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package lang.immutable;
public class Member {
// 일반 객체
private Address address;
// 불변 객체
private ImmutableAddress address2;
public Member(Address address) {
this.address = address;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
public ImmutableAddress getAddress2() {
return address2;
}
public void setAddress2(ImmutableAddress address2) {
this.address2 = address2;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package lang.immutable;
public class Address {
private String value;
public Address(String value) {
this.value = value;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package lang.immutable;
public class ImmutableAddress {
private final String value;
public ImmutableAddress(String value) {
this.value = value;
}
public String getValue() {
return value;
}
public ImmutableAddress withValue(String value){
return new ImmutableAddress(value);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package lang.immutable;
public class ImmutableMain {
public static void main(String[] args) {
Address address1 = new Address("서울");
Member a = new Member(address1);
Member b = new Member(address1);
System.out.println("a의 주소: " + a.getAddress().getValue()); // 서울
System.out.println("b의 주소: " + b.getAddress().getValue()); // 서울
// b가 포항으로 이사감
b.getAddress().setValue("포항");
System.out.println("a의 주소: " + a.getAddress().getValue()); // 포항
System.out.println("b의 주소: " + b.getAddress().getValue()); // 포항
// 불변 객체 예시
ImmutableAddress immutableAddress = new ImmutableAddress("서울");
a.setAddress2(immutableAddress);
b.setAddress2(immutableAddress);
System.out.println("a의 주소: " + a.getAddress2().getValue()); // 서울
System.out.println("b의 주소: " + b.getAddress2().getValue()); // 서울
//b의 이사
ImmutableAddress newAddress = b.getAddress2().withValue("포항");
b.setAddress2(newAddress);
System.out.println("a의 주소: " + a.getAddress2().getValue()); // 서울
System.out.println("b의 주소: " + b.getAddress2().getValue()); // 포항
}
}