티스토리 뷰
불변 객체란?
객체의 상태(내부의 값, 멤버 변수)가 변하지 않는 객체를 불변 객체라고 한다.
불변 객체 사용X 예제
Book.java
public class Book {
private String name;
public Book(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Main.java
public class Main {
public static void main(String[] args) {
Book book1 = new Book("JPA");
Book book2 = book1;
System.out.println("----------");
System.out.println("book2 책 이름 변경");
book2.setName("Spring Data JPA");
System.out.println("----------");
System.out.println("book1.name: " + book1.getName());
System.out.println("book2.name: " + book2.getName());
}
}
실행 결과
----------
book2 책 이름 변경
----------
book1.name: Spring Data JPA
book2.name: Spring Data JPA
위의 Main의 코드에서 book2의 이름만 변경 했는데 book1의 이름도 같이 변경된 것을 확인할 수 있다. 이는 book1의 참조 변수와 book2 참조형 변수의 참조 값이 공유했기 때문에 발생한 것이다.(아래 이미지 참조.) 참고로 이를 사이드 이펙트 발생이라고 보며 사이드 이펙트는 어떤 하나의 계산으로 인해 추가적인 부수 효과를 발생한 것을 의미한다.

참조 공유로 인한 사이드 이펙트 해결 방안
위의 사이드 이펙트를 해결할 방안은 Book 객체를 불변 객체를 바꾸고 만약 book2의 이름을 바꾼다면 새로운 객체를 생성해야 한다.
불변객체 사용O 예제
ImmutableBook.java
public class ImmutableBook {
private final String name;
public ImmutableBook(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
위의 불변 객체를 보면 멤버 변수에 final을 선언한 것을 볼 수 있다. final은 객체 생성 후 변경 불가능을 의미하며, 오직 생성자를 통해서 name을 무조건 초기화 해줘야한다. 그리고 setter 메서드가 없는 것을 볼 수 있는데, final이 선언되었기 때문에 setter 있을 경우 컴파일 오류가 발생한다. 추가로 final이 꼭 있을 필요는 없지만 만약 final을 생략한다면 setter와 같이 멤버 변수를 수정할 수 있는 메서드가 없어야한다.
Main.java
public class Main {
public static void main(String[] args) {
ImmutableBook book1 = new ImmutableBook("JPA");
ImmutableBook book2 = book1;
System.out.println("----------");
System.out.println("book2 책 이름 변경");
//book2.setName("Spring Data JPA"); 컴파일 오류 발생
book2 = new ImmutableBook("Spring Data JPA"); // 새로운 인스턴스를 생성
System.out.println("----------");
System.out.println("book1.name: " + book1.getName());
System.out.println("book2.name: " + book2.getName());
}
}
실행 결과
----------
book2 책 이름 변경
----------
book1.name: JPA
book2.name: Spring Data JPA
위의 Main 코드를 보면 중간에 새로운 ImmutableBook 인스턴스를 생성한 것을 볼 수 있다. 이렇게 새로 객체를 생성하면 앞서 book1의 name이 같이 수정 되는 사이드 이펙트가 발생하지 않게 된다.
불변 객체 값 변경
앞에서는 불변 객체는 값 변경이 불가능하다고 했는데, 만약 불변 객체의 값을 변경해야한다면 어떻게 해야할까? 아래의 예제를 통해 알아보았다.
불변 객체 값 변경 예제
public class ImmutableBook {
private final String name;
public ImmutableBook(String name) {
this.name = name;
}
public String getName() {
return name;
}
public ImmutableBook changeName(String changeName) {
return new ImmutableBook(changeName);
}
}
Main.java
public class Main {
public static void main(String[] args) {
ImmutableBook book1 = new ImmutableBook("JPA");
ImmutableBook book2 = book1;
System.out.println("----------");
System.out.println("book2 책 이름 변경");
// 불변 객체가 값을 변경한 것처럼 보이지만 사실 새로운 인스턴스를 반환.
book2 = book1.changeName("Spring Data JPA");
System.out.println("----------");
System.out.println("book1.name: " + book1.getName());
System.out.println("book2.name: " + book2.getName());
}
}
실행 결과
----------
book2 책 이름 변경
----------
book1.name: JPA
book2.name: Spring Data JPA
위의 ImmutableBook의 changeName 메서드를 보면 새로운 ImmutableBook 객체를 반환하는 것을 볼 수 있으며, 메서드를 통해 기존의 불변도 유지하면서 새로운 결과도 만들 수 있다. 한마디로 불변 객체는 그 객체 내의 값을 변경할 수 없다. 하지만 값을 변경하고 싶으면 새로운 객체를 생성 후 반환하면 된다.
정리
객체의 공유 참조는 막을 수 없다. 그래서 사이드 이펙트가 발생하기도 하는데, 이를 방지하기 위해 불변 객체를 사용하면 된다. 불변 객체를 사용하면 처음 상태의 값은 유지되고 변경하고 싶은 객체는 새로운 불변 객체를 생성해서 반환하면 된다. 그러면 처음 객체에 영향은 없고 사이드 이펙트가 발생하지 않게 된다.
본 포스팅은 “김영한의 실전 자바 - 중급 1편/인프런”를 학습한 내용을 정리한 것
'Java > Java' 카테고리의 다른 글
| <Java> volatile이란? (0) | 2024.10.18 |
|---|---|
| <Java> Thread join() 메서드란? (3) | 2024.10.16 |
| <Java> 해시 자료 구조에서 equals(), hashCode() 메서드의 중요성 (0) | 2024.07.01 |
| <Java> 열거형(ENUM)이 생긴 이유? (0) | 2024.06.20 |
| <Java> OCP(Open-Closed Principle) 원칙이란? (0) | 2024.05.22 |
