불변 객체 (Immutable Object)란?
💡 자바(Java)를 이용한 OOP에서 불변 객체는 생성된 후에,
상태를 변경할 수 없는 객체를 의미한다.
읽기 전용(read-only) 메서드만을 제공하며, 인스턴스화 되면 내부 상태를 변경할 수 없다.
사전적으로 사물의 모양이나 성질이 달라질 수 없음을 의미한다.
👌 참고 - 가변 객체 (Mutable Object) vs 불변 객체 (Immutable Object)
➡️ 가변은 이름 그대로 처음 만든 이후 상태가 변할 수 있다는 의미이다.
➡️ 불변은 이름 그대로 처음 만든 이후 상태가 변하지 않는다는 의미이다.
특징
- 캐시 안정성 (Caching)
상태가 고정되어 있으므로, 캐시할 수 있다. 동일한 상태의 객체가 재사용되는 상황에서
성능 향상으로도 이어질 수 있다. - 멀티 쓰레드 안정성 (Multi-Thread safety)
여러 스레드가 동시에 수정할 위험 없이 접근할 수 있기때문에, 본질적으로 안전하다.
즉, 멀티 쓰레드에 유용하며, 동기화를 고려하지 않아도 된다. - GC (Garbage Collection) 성능 향상
생성되면 수정되지 않기에, 기존 개체를 재사용하며 객체의 생성 및 GC의 수집 빈도를 줄여준다.
상태가 고정되머 있으므로, GC가 내부 객체 참조를 추적할 필요가 없다. - 보안 (Security)
보안이 중요한 상황에서 자주 사용되는데, 문자열 조작에 불변 객체를 사용하면
특정 유형의 보안 취약점을 방지할 수 있다. - 데이터 불변성 (Data Immutability)
상태를 변경할 수 없으므로, 데이터 무결성을 유지하는데 도움이 된다.
예상치 못한 수정에 대해 걱정 할 필요가없기에, 코드의 신뢰성을 높여준다.
코드 예시
예시를 보기전에 선언 방법 3가지에 대해 알아보려한다.
➡️ setter를 사용하지 않는다. (객체의 상태를 변경하는 메서드 미제공)
➡️ private으로 선언한다.
➡️ final을 선언한다.
⬇️ 하단 간단한 예시를 통해 자세히 알아보려한다.
Address.java
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;
}
@Override
public String toString() {
return "Address{" +
"value='" + value + '\'' +
'}';
}
}
ImmutableAddress.java
public class ImmutableAddress {
// final 불변 객체
private final String value;
public ImmutableAddress(String value) {
this.value = value;
}
public String getValue() {
return value;
}
@Override
public String toString() {
return "Address{" +
"value='" + value + '\'' +
'}';
}
}
불변 객체 실행 코드
public class RefMain2 {
public static void main(String[] args) {
// 참조형 변수는 하나의 인스턴스를 공유할 수 있다.
ImmutableAddress a = new ImmutableAddress("서울"); // x001
ImmutableAddress b = a; // x001
// toString()을 재정의 했기에, 변수 값 출력
System.out.println("a = " + a); // 실행 결과 ➡️ Address{value='서울'}
System.out.println("b = " + b); // 실행 결과 ➡️ Address{value='서울'}
/**
* b의 값을 부산으로 변경 : final 객체이기때문에
* setter가 존재하지 않음.
* 즉, 새로운 인스턴스를 생성해서 할당해야한다.
* b.setValue("부산");
* */
b = new ImmutableAddress("부산");
System.out.println("부산 -> b");
System.out.println("a = " + a); // 실행 결과 ➡️ Address{value='서울'}
System.out.println("b = " + b); // 실행 결과 ➡️ Address{value='부산'}
}
}
실행 결과
a = Address{value='서울'}
b = Address{value='서울'}
부산 -> b
a = Address{value='서울'}
b = Address{value='부산'}
➡️ ImmutableAddress의 경우 값을 변경할 수 있는 b.setValue() 메서드 자체가 제거되었다.
➡️ setter() 메서드가 없음에 따라, 값이 변경 불가능하다는 사실을 알게되고, 불변 객체인 사실까지 깨달을 수 있다.
예를 들어 b.setValue("부산")을 호출하려고 했는데, 해당 메서드가 없기에 컴파일 오류가 발생한다.
➡️ 따라서 새로운 ImmutableAddress("부산") 인스턴스를 생성해서 b에 대입한다.
➡️ 결과적으로 a, b는 서로 다른 인스턴스를 참조하고, a가 참조하던 ImmutableAddress는 그대로 유지된다.
⬇️ 하단 조금 더 복잡하고 의미있는 예시를 통해 자세히 알아보려한다.
불변 객체를 사용하지만 값을 변경해야하는 상황이 발생하면 어떻게 해야할까?
예를 들어서 기존 값에 새로운 값을 더하는 add()와 같은 메서드가 있다.
public class ImmutableObj {
private final int value;
public ImmutableObj(int value) {
this.value = value;
}
public ImmutableObj add(int addValue) {
int result = value + addValue;
return new ImmutableObj(result);
}
public int getValue() {
return value;
}
}
➡️ 여기서 핵심은 add() 메서드이다.
➡️ 불변 객체는 값을 변경하면 안된다. 그러면 이미 불변 객체가 아닌걸까?
➡️ 하지만 위 예제 소스에서는 기존 값에 새로운 값을 더해야 한다.
➡️ 기존 값은 변경하지 않고, 대신 계산 결과를 바탕으로 새로운 객체를 만들어서 반환한다.
➡️ 이렇게하면 불변도 유지하면서 새로운 결과도 만들 수 있다.
public class ImmutableMain1 {
public static void main(String[] args) {
ImmutableObj obj1 = new ImmutableObj(10);
ImmutableObj obj2 = obj1.add(20);
// 계산 이후에도 기존값과 신규값 모두 확인 가능
System.out.println("obj1 = " + obj1.getValue());
System.out.println("obj2 = " + obj2.getValue());
}
}
실행 결과
obj1 = 10
obj2 = 30
➡️ add(20)을 호출한다.
➡️ 기존 객체에 있는 10과 인수로 입력한 20을 더한다.
이때 기존 객체의 값을 변경하면 안되므로 계산 결과를 기반으로
새로운 객체를 만들어서 반환한다.
➡️ 새로운 객체는 x002 참조를 가진다. 새로운 객체의 참조값을 obj2에 대입한다.
⚠️ 만일 새로 생성된 반환 값을 사용하지 않으면 어떻게 될까?
public class ImmutableMain2 {
public static void main(String[] args) {
ImmutableObj obj1 = new ImmutableObj(10);
obj1.add(20);
/**
* 불변 객체에서 변경과 관련된 메서드들은 보통 새로운 객체를 반환하기때문에,
* 꼭 반환 값을 받아야 한다.
* */
System.out.println("obj1 = " + obj1.getValue());
}
}
실행 결과
obj1 = 10
실행 결과처럼 아무것도 처리되지 않은 것 처럼 보인다.
불변 객체에서 변경과 관련된 모든 메서드들은 보통 객체를 새로 만들어서 반환하기 때문에
반드시 반환 값을 받아야한다.
OUTRO
오늘은 불변 객체에 대해서 알아봤습니다.
저번 포스팅 참조형 타입에서 객체 간 공유를 막을 방법이 없기에 사이드 이펙트가 발생했다는 내용이 있었는데,
[Java] 기본형과 참조형(Primitive Type & Reference Type)
데이터 타입 https://bit.ly/4cMZkYh [Java] 변수와 데이터 타입이란? 변수(variable)란? 💡말 그대로 변하는 수 즉, 고정되지 않은 수를 변수라고 의미한다. 프로그래밍 언어에선 변수란 값을 저장할 수 있
hyun-dev-com.tistory.com
불변객체와 이어지는 내용이므로 한번 참고해보시길 바랍니다.
그러면 다음 포스팅에서 뵙겠습니다.
위 포스팅 글은 김영한님의 실전 자바 - 중급 1편을 참고했습니다.
김영한의 실전 자바 - 중급 1편 | 김영한 - 인프런
김영한 | 실무에 필요한 자바의 다양한 중급 기능을 예제 코드로 깊이있게 학습합니다., [사진]국내 개발 분야 누적 수강생 1위, 제대로 만든 김영한의 실전 자바[사진][임베딩 영상]단순히 자바
www.inflearn.com
'Language > Java' 카테고리의 다른 글
[Java] 조건문 (if문, if-else, switch-case) (0) | 2024.04.16 |
---|---|
[Java] String (문자열) 이란? + (특징, 클래스 구조, 메서드) (0) | 2024.04.12 |
[Java] 기본형과 참조형(Primitive Type & Reference Type) (0) | 2024.04.06 |
[Java] 클래스, 객체, 메서드, 생성자 (0) | 2024.03.30 |
[Java] 연산자 (Operator) (0) | 2024.03.29 |