본문 바로가기

Programming/Java

[Java] 김영한의 자바 중급 1편 #2 - 불변 객체

반응형

 

객체의 상태, 즉 객체 내부의 값이나 필드, 멤버 변수가 변하지 않는 객체를 불변 객체라고 한다.

그렇다면 불볍 객체를 왜 사용하는 것일까? 기본형과 참조형의 공유에 대해서 먼저 알아본다.

 


1. 기본형과 참조형의 공유

자바의 데이터 타입을 보면 크게 다음의 타입으로 나눌 수 있다.

  • 기본형 : 하나의 값을 여러 변수에서 절대로 공유하지 않음
  • 참조형 : 하나의 객체를 참조값을 통해 여러 변수에서 공유함

 

1.1. 기본형 예제

기본형 : 하나의 값을 여러 변수에서 절대로 공유하지 않는다.

→ b = a 라고 하면 자바는 항상 값을 복사해서 대입한다.

 

 

 

 

1.2. 참조형 예제

  • b = a 라고 할 경우 a라는 인스턴스의 메모리 참조값인 x001을 b에 대입하는 것이다.

 

참조형 예제에서 볼 수 있듯이, 여러 변수가 하나의 객체를 공유하는 것을 막을 방법은 없다.

 

→ 변수 a, b가 서로 각각 새로운 인스턴스를 생성하면 서로 다른 참조값을 바라보기 때문에 해결되긴 하지만 이를 강제할 수 있는 방법은 현재로써는 없다.

 

그럼 공유 참조로 발생하는 문제를 어떻게 해결할 수 있는 것인가?

 

 


2. 불변 객체 - 도입

지금까지 발생한 문제를 잘 생각해보면 공유하면 안되는 객체를 여러 변수에서 공유했기 때문에 발생한 문제이다.

하지만 살펴보았듯이 객체의 공유를 막을 수 있는 방법은 없다.

 

즉, 문제의 직접적인 원인은 공유된 객체의 값을 변경한 것에 있다.

 

불변 객체 : 객체의 상태(객체 내부의 값, 필드, 멤버 변수)가 변하지 않는 객체이다.

 

앞서 만들었던 Address 클래스를 Immutable Class로 만들어보자.

 

 

 

 

  • ImmutableAddress 내 value는 final 타입이기 때문에 값을 변경할 수 없다.
    • 따라서 setValue() 메서드 자체를 사용할 수 없다.
  • 따라서 b라는 객체 내 value 값을 변경하기 위해서는 새로 인스턴스를 생성해서 b 값에 대입해줘야 한다.
  • 결과적으로 a, b는 서로 다른 인스턴스를 참조하고 a가 참조하던 ImmutableAddress는 그대로 유지된다.

 

2.1. 정리

불변이라는 단순한 제약을 통해 사이드 이펙트를 막을 수 있다.

→ 객체의 공유 참조는 막을 수 없다. 그래서 객체의 값을 변경하면 다른 곳에서 참조하는 변수의 값도 함께 변경되는 사이드 이펙트가 발생한다. 사이드 이펙트가 발생하면 안되는 상황일 경우 불변 객체를 만들어서 사용하면 된다.

 

2.2. 참고 - 가변 객체 vs 불변 객체

Address는 가변 클래스이다. 이 클래스로 객체를 생성하면 가변 객체가 된다.

ImmutableAddress는 불변 클래스이다. 이 클래스로 객체를 생성하면 불변 객체가 된다.

 

 


4. 불변 객체 - 심화 예제

조금 더 심화적인 예제를 확인해본다.

 

 

 

 

  • MemberV2는 주소를 변경할 수 없는 ImmutableAddress 클래스를 멤버 변수로 사용한다.
  • MemberB의 주소를 변경할 때 setValue의 매개변수로 (new ImmutableAddress(”부산”)) 이라는 새로운 인스턴스를 생성한 뒤 새로운 참조값을 전달한다. → MemberA는 기존 주소를 그대로 유지할 수 있게 된다.

 

 


5. 불변 객체의 값 변경

불변 객체를 사용하지만 그대로 값을 변경해야 하는 메서드가 필요하다면?

 

5.1. 가변 객체에서는 어떻게 할까?

 

 

  • MutableObj을 새로 생성하고 10이란 값을 넣어준다.
  • obj(참조값)에 있는 add 메서드에 20을 매개변수로 전달한다.
    • 인스턴스 변수의 값인 value에 addValue(20)이 전달되어 30으로 변경된다.
  • obj.getValue()를 호출하면 30이 출력된다.

 

5.2. 불변 객체에서는 어떻게 하는가?

 

불변 객체를 설계할 때 기존 값을 변경해야 하는 메서드가 필요할 수 있다.

→ 이럴 때 기존 객체의 값은 그대로 두고 변경된 결과를 새로운 객체에 담아서 return 값으로 반환하면 된다.

→ 결과를 보면 기존 값은 그대로 유지되는 것을 확인할 수 있다.

 

 

 

  1. add(20) 메서드를 호출한다.
  2. 기존 객체의 인스턴스 변수(value) 값인 10과 인수로 전달된 20을 result 변수로 담아둔다.
    1. 이 때 기존 객체의 값을 변경할 수 없기에 계산 결과를 기반으로 새로운 객체를 만들어서 반환한다.
  3. 새로운 객체는 x002 참조를 가진다. 새로운 객체의 참조값을 obj2에 대입해준다.

 

 

 

반응형