불변 클래스?


답변:


135

불변 객체 란 무엇입니까?

불변 객체는 인스턴스화 된 후에 상태를 변경하지 않는 객체입니다.

개체를 변경 불가능하게 만드는 방법은 무엇입니까?

일반적으로 불변 객체는 노출 된 멤버가없고 setter가없는 클래스를 정의하여 만들 수 있습니다.

다음 클래스는 불변 객체를 만듭니다.

class ImmutableInt {
  private final int value;

  public ImmutableInt(int i) {
    value = i;
  }

  public int getValue() {
    return value;
  }
}

위의 예에서 볼 수 있듯이의 값은 ImmutableInt객체가 인스턴스화 될 때만 설정 될 수 있으며 getter ( getValue) 만 있으면 인스턴스화 후에 객체의 상태를 변경할 수 없습니다.

그러나 개체가 참조하는 모든 개체도 변경 불가능해야하며 그렇지 않으면 개체의 상태를 변경할 수 있다는 점에주의해야합니다.

예를 들어 배열에 대한 참조를 허용하거나 ArrayListgetter를 통해 가져올 수 있도록하면 배열 또는 컬렉션을 변경하여 내부 상태를 변경할 수 있습니다.

class NotQuiteImmutableList<T> {
  private final List<T> list;

  public NotQuiteImmutableList(List<T> list) {
    // creates a new ArrayList and keeps a reference to it.
    this.list = new ArrayList(list); 
  }

  public List<T> getList() {
    return list;
  }
}

위 코드의 문제점 ArrayList은를 통해 획득 getList하고 조작 할 수있어 객체 자체의 상태가 변경되어 불변이 아니라는 것입니다.

// notQuiteImmutableList contains "a", "b", "c"
List<String> notQuiteImmutableList= new NotQuiteImmutableList(Arrays.asList("a", "b", "c"));

// now the list contains "a", "b", "c", "d" -- this list is mutable.
notQuiteImmutableList.getList().add("d");

이 문제를 해결하는 한 가지 방법은 getter에서 호출 될 때 배열 또는 컬렉션의 복사본을 반환하는 것입니다.

public List<T> getList() {
  // return a copy of the list so the internal state cannot be altered
  return new ArrayList(list);
}

불변성의 장점은 무엇입니까?

불변성의 장점은 동시성과 함께 제공됩니다. 여러 스레드가 동일한 객체의 상태를 변경하려고 시도 할 수 있기 때문에 변경 가능한 객체의 정확성을 유지하는 것은 어렵습니다. 일부 스레드는 해당 객체에 대한 읽기 및 쓰기의 타이밍에 따라 동일한 객체의 다른 상태를 보게됩니다. 목적.

불변 객체를 가짐으로써 불변 객체의 상태가 변경되지 않으므로 객체를보고있는 모든 스레드가 동일한 상태를 보게 될 것입니다.


5
스레드 안전성 (동시성에 중요)이 불변성의 유일한 장점은 아닙니다. 그것은 또한 당신이 개체의 방어적인 복사본을 만들 필요가 없다는 것을 의미하며, 수정되지 않아야하는 개체를 실수로 수정할 수 없기 때문에 버그를 방지합니다.
Jesper

7
목록의 복사본을 반환하는 대신 목록에서 return Collections.unmodifiableList(list);읽기 전용보기를 반환 할 수도 있습니다 .
Jesper

18
수업도 만들어야 final합니다. 그렇지 않으면 setter 메서드 (또는 다른 종류의 변경 방법 및 변경 가능한 필드)로 확장 할 수 있습니다.
Abhinav Sarkar

6
@AbhinavSarkar 하위 클래스에서 액세스 할 수 없으므로 final클래스에 private필드 만 있는 경우에는 그럴 필요가 없습니다.
icza 2014 년

3
@icza 클래스는 최종이어야합니다. 공용 getter 메서드가 있기 때문에 클래스를 확장하고 getter 메서드를 재정의 한 다음 우리 방식으로 필드를 변경할 수 있습니다. 그래서 더 이상 변경할 수 없습니다
sunil

19

이미 주어진 답변 외에도, 놓치기 쉬운 세부 사항 (예 : 방어 적 사본)이 있으므로 Effective Java, 2nd Ed.의 불변성에 대해 읽는 것이 좋습니다. 또한, Effective Java 2nd Ed. 모든 Java 개발자가 반드시 읽어야 할 문서입니다.


3
이것은 살펴볼 정확한 리소스입니다. 언급 된 이점은 오류가 발생하기 쉬운 코드에서 스레드 안전성에 이르기까지 다양합니다.
gpampara 2010

6

다음과 같이 클래스를 불변으로 만듭니다.

public final class Immutable
{
    private final String name;

    public Immutable(String name) 
    {
        this.name = name;
    }

    public String getName() { return this.name; } 

    // No setter;
}

다음은 Java 클래스를 변경 불가능하게 만들기위한 요구 사항입니다.

  • 클래스 를 다음과 같이 선언해야합니다 final(하위 클래스를 만들 수 없음).
  • 클래스의 멤버 는 다음과 같이 선언해야합니다 final(객체 생성 후 값을 변경할 수 없도록)
  • 멤버 값 을 가져 오기 위해 모든 변수에 대해 Getter 메서드작성 합니다.
  • Setters 메서드 없음

불변 클래스는
스레드로부터 안전 하기 때문에 유용 합니다.
-그들은 또한 당신의 디자인에 대해 깊은 것을 표현합니다 : "이것을 바꿀 수 없습니다.", 그것이 적용될 때, 그것은 당신이 필요한 것입니다.


5

불변성은 주로 두 가지 방법으로 달성 할 수 있습니다.

  • final재 할당을 피하기 위해 인스턴스 속성 사용
  • 클래스 내부의 내용을 수정할 수있는 작업을 허용하지 않는 클래스 인터페이스를 사용합니다 ( 게터 만 사용 하고 세터 없음

불변성의 장점은 이러한 객체에 대해 만들 수있는 가정입니다.

  • 부작용이없는 규칙 (함수 프로그래밍 언어에서 정말 인기가 있음)을 얻고 동시 환경에서 객체를 더 쉽게 사용할 수 있습니다. 객체가있을 때 원자 적 또는 비원 자적 방식으로 변경할 수 없다는 것을 알고 있기 때문입니다. 많은 스레드에서 사용
  • 언어 구현은 이러한 객체를 다른 방식으로 처리하여 정적 데이터에 사용되는 메모리 영역에 배치하여 이러한 객체를 더 빠르고 안전하게 사용할 수 있도록합니다 (이는 문자열의 JVM 내부에서 발생 함).

불변성은 동등한 부작용이없는 것과 동일하지 않습니다. 예를 들어, 변경 불가능한 객체는 파일 로깅과 같은 부작용을 일으킬 수 있습니다. 객체를 불변으로 만들면 부작용이 없다고 말하는 것은 약간 정확하지 않습니다.
Grundlefleck

1
@Grundleflek, 나는 이것이 머리카락을 나누고 있다고 생각합니다. 계약의 일부로 로그 파일을 수정하고 다른 클래스에서 해당 로그 파일에 액세스 할 수있는 경우 클래스는 변경 불가능하지 않습니다. 로그 파일이 다른 클래스에서 숨겨져 있고 클래스 계약의 일부가 아닌 경우 클래스는 사실상 변경 불가능하며 실제로 모든 의도와 목적에 부작용이 없습니다. Wikipedia 부작용 페이지의 (비 소스) 소개에는 "... 값을 생성하는 것 외에도 일부 상태를 수정하거나 호출 함수와 관찰 가능한 상호 작용이있는 경우 표현식은 부작용이 있다고합니다."
Jeff Axelrod 2011-06-23

3

불변 클래스는 인스턴스화 된 후에 값을 재 할당 할 수 없습니다. 생성자는 해당 전용 변수에 값을 할당합니다. 개체가 null이 될 때까지 setter 메서드를 사용할 수 없기 때문에 값을 변경할 수 없습니다.

불변하기 위해서는 다음을 만족해야합니다.

  • Al 변수는 private 이어야합니다 .
  • mutator 메서드 (세터)가 제공 되지 않습니다 .
  • class final (Strong Immutability) 또는 메서드를 final (Week immutability)로 만들어 메서드 재정의를 피하십시오.
  • 기본이 아니거나 변경 가능한 클래스가 포함 된 경우 깊이 복제하십시오.

/**
* Strong immutability - by making class final
*/
public final class TestImmutablity {

// make the variables private
private String Name;

//assign value when the object created
public TestImmutablity(String name) {
this.Name = name;
}

//provide getters to access values
public String getName() {

return this.Name;
}
}

장점 : 불변 객체는 죽을 때까지 초기화 된 값을 포함합니다.

java-immutable-classes-short-note


2

불변 클래스 는 생성 후 객체를 변경할 수없는 클래스 입니다.

불변 클래스는 다음과 같은 경우에 유용합니다.

  • 캐싱 목적
  • 동시 환경 (ThreadSafe)
  • 상속하기 어렵다
  • 어떤 환경에서도 값을 변경할 수 없습니다.

문자열 클래스

코드 예

public final class Student {
    private final String name;
    private final String rollNumber;

    public Student(String name, String rollNumber) {
        this.name = name;
        this.rollNumber = rollNumber;
    }

    public String getName() {
        return this.name;
    }

    public String getRollNumber() {
        return this.rollNumber;
    }
}

2

Java 클래스를 불변으로 만들 수있는 방법은 무엇입니까?

JEP 359 가있는 JDK 14+부터 " records"를 사용할 수 있습니다 . Immutable 클래스를 만드는 가장 간단하고 번거로운 방법입니다.

레코드 클래스는 레코드 에 대한 설명을 제공하는 레코드로 알려진 고정 필드 세트에 대한 얕은 불변 의 투명한 캐리어입니다 . 각각 은 제공된 값을 보유 하는 필드와 값 을 검색 하는 방법을 제공 합니다. 필드 이름과 접근 자 이름은 구성 요소의 이름과 일치합니다.componentsstatecomponentfinalaccessor

불변의 직사각형을 만드는 예를 고려해 보겠습니다.

record Rectangle(double length, double width) {}

생성자를 선언 할 필요가 없으며 equals 및 hashCode 메서드를 구현할 필요가 없습니다. 모든 기록에는 이름과 상태 설명이 필요합니다.

var rectangle = new Rectangle(7.1, 8.9);
System.out.print(rectangle.length()); // prints 7.1

객체 생성 중에 값의 유효성을 검사하려면 생성자를 명시 적으로 선언해야합니다.

public Rectangle {

    if (length <= 0.0) {
      throw new IllegalArgumentException();
    }
  }

레코드의 본문은 정적 메서드, 정적 필드, 정적 이니셜 라이저, 생성자, 인스턴스 메서드 및 중첩 유형을 선언 할 수 있습니다.

인스턴스 방법

record Rectangle(double length, double width) {

  public double area() {
    return this.length * this.width;
  }
}

정적 필드, 메서드

상태는 구성 요소의 일부 여야하므로 레코드에 인스턴스 필드를 추가 할 수 없습니다. 그러나 정적 필드와 메서드를 추가 할 수 있습니다.

record Rectangle(double length, double width) {

  static double aStaticField;

  static void aStaticMethod() {
    System.out.println("Hello Static");
  }
}

불변성의 필요성은 무엇이며 이것을 사용하면 어떤 이점이 있습니까?

이전에 게시 된 답변은 불변성의 필요성을 정당화하기에 충분하며 전문가입니다.


0

불변 객체를 만드는 또 다른 방법은 Immutables.org 라이브러리를 사용하는 것입니다.

필요한 종속성이 추가되었다고 가정하고 추상 접근 자 메서드를 사용하여 추상 클래스를 만듭니다. 인터페이스 또는 주석 (@interface)으로 주석을 달아도 동일한 작업을 수행 할 수 있습니다.

package info.sample;

import java.util.List;
import java.util.Set;
import org.immutables.value.Value;

@Value.Immutable
public abstract class FoobarValue {
  public abstract int foo();
  public abstract String bar();
  public abstract List<Integer> buz();
  public abstract Set<Long> crux();
}

이제 생성 된 변경 불가능한 구현을 생성하고 사용할 수 있습니다.

package info.sample;

import java.util.List;

public class FoobarValueMain {
  public static void main(String... args) {
    FoobarValue value = ImmutableFoobarValue.builder()
        .foo(2)
        .bar("Bar")
        .addBuz(1, 3, 4)
        .build(); // FoobarValue{foo=2, bar=Bar, buz=[1, 3, 4], crux={}}

    int foo = value.foo(); // 2

    List<Integer> buz = value.buz(); // ImmutableList.of(1, 3, 4)
  }
}

0

불변 클래스는 단순히 인스턴스를 수정할 수없는 클래스입니다.

각 인스턴스에 포함 된 모든 정보는 개체의 수명 동안 고정되므로 변경 사항을 관찰 할 수 없습니다.

변경 불가능한 클래스는 변경 가능한 클래스보다 디자인, 구현 및 사용이 더 쉽습니다.

클래스를 변경 불가능하게 만들려면 다음 5 가지 규칙을 따르십시오.

  1. 개체의 상태를 수정하는 메서드를 제공하지 마세요.

  2. 클래스를 확장 할 수 없는지 확인하십시오.

  3. 모든 필드를 최종적으로 만드십시오.

  4. 모든 필드를 비공개로 설정합니다.

  5. 변경 가능한 구성 요소에 대한 독점적 인 액세스를 보장합니다.

변경 불가능한 객체는 본질적으로 스레드로부터 안전합니다. 동기화가 필요하지 않습니다.

변경 불가능한 객체는 자유롭게 공유 할 수 있습니다.

불변 객체는 다른 객체를위한 훌륭한 빌딩 블록을 만듭니다.


0

@Jack, 클래스에 최종 필드와 setter가 있어도 클래스를 변경할 수 없습니다. final 키워드는 변수가 재 할당되지 않도록합니다. getter 메서드에서 모든 필드의 전체 복사본을 반환해야합니다. 이것은 getter 메소드에서 객체를 가져온 후 객체의 내부 상태가 방해받지 않도록합니다.


-1

영어가 모국어가 아닌 사람으로서 나는 "불변 클래스"가 "구성된 클래스 객체는 불변"이라는 일반적인 해석을 싫어합니다. 오히려 나는 "클래스 객체 자체는 불변"이라고 해석하기를 원합니다.

즉, "불변 클래스"는 일종의 불변 객체입니다. 차이점은 혜택이 무엇인지 대답 할 때입니다. 내 지식 / 해석에 따르면 불변 클래스는 객체의 런타임 동작 수정을 방지합니다.


-1

여기에있는 대부분의 답변은 훌륭하고 일부는 규칙을 언급했지만 이러한 규칙을 따라야하는 이유와시기를 말로 적어 두는 것이 좋습니다. 그래서 아래 설명을하고 있습니다

  • 멤버 변수를 'final'로 선언 – final로 선언하면 컴파일러에서 강제로 초기화합니다. 기본 생성자, arg 생성자 (아래 샘플 코드 참조)에 의해 직접 초기화 할 수 있으며 초기화 후에는 최종적으로 수정할 수 없습니다.
  • 물론 최종 변수에 Setter를 사용하려고하면 컴파일러에서 오류가 발생합니다.

    public class ImmutableClassExplored {
    
        public final int a; 
        public final int b;
    
        /* OR  
        Generally we declare all properties as private, but declaring them as public 
        will not cause any issues in our scenario if we make them final     
        public final int a = 109;
        public final int b = 189;
    
         */
        ImmutableClassExplored(){
            this. a = 111;
            this.b = 222;
        }
    
        ImmutableClassExplored(int a, int b){
            this.a = a;
            this.b= b;
        }
    }
    

클래스를 '최종'으로 선언해야합니까?

  • 클래스 선언에 final 키워드가 없으면 클래스를 상속받을 수 있습니다. 따라서 하위 클래스는 getter 메서드를 재정의 할 수 있습니다. 여기서 우리는 두 가지 시나리오를 고려해야합니다.

1. 프리미티브 멤버 만 있는 경우 : 문제가 없습니다. 클래스에 프리미티브 멤버 만 있으면 클래스를 final로 선언 할 필요가 없습니다.

2. 객체를 멤버 변수로 사용 : 객체를 구성원 변수로 사용하는 경우 해당 객체의 구성원도 최종적으로 만들어야합니다. 우리가 트리를 깊숙이 탐색하고 항상 가능하지 않을 수도있는 모든 객체 / 원본을 최종으로 만들어야 함을 의미합니다. 따라서 해결 방법은 상속을 방지하는 최종 클래스를 만드는 것입니다. 따라서 getter 메서드를 재정의하는 하위 클래스에 대한 질문이 없습니다.


-1

Lombok의 @Value 주석을 사용하여 불변 클래스를 생성 할 수 있습니다. 아래 코드처럼 간단합니다.

@Value
public class LombokImmutable {
    int id;
    String name;
}

Lombok 사이트의 문서에 따라 :

@Value는 @Data의 변경 불가능한 변형입니다. 모든 필드는 기본적으로 비공개 및 최종으로 만들어지며 setter는 생성되지 않습니다. 불변성은 하위 클래스에 강제 할 수있는 것이 아니기 때문에 클래스 자체도 기본적으로 최종화됩니다. @Data와 마찬가지로 유용한 toString (), equals () 및 hashCode () 메서드도 생성되고, 각 필드는 getter 메서드를 가져오고, 모든 인수를 포함하는 생성자 (필드 선언에서 초기화 된 최종 필드 제외)도 생성됩니다. .

완전히 작동하는 예제는 여기 에서 찾을 수 있습니다.


수락 된 답변 (녹색을 찾으세요 ✓)과 다른 답변이있는 이전 질문에 답변하기 전에 답변이 새로운 내용을 추가하거나 그와 관련하여 도움이되는지 확인하십시오. OP의 질문에 답할 때주의하십시오 Java 클래스를 불변으로 만들 수있는 방법, 불변성의 필요성은 무엇이며 이것을 사용하는 데 이점이 있습니까? . – 당신은 단지 OP의 질문이 무엇에 관한 것인지는 아닌 써드 파티 프레임 워크 / 라이브러리 인 Lombok을 사용할 수 있다는 부분적인 대답만을 제공하고 있습니다. 또한 Java 15가 나왔습니다. Java를 사용할 수 있는데 왜 Lombok을 record사용합니까?
Ivo Mori

이를 달성하는 방법에 대한 옵션 중 하나를 제공합니다. 모든 사람이 Java 15를 사용하는 것은 아니며 오늘날 대부분의 응용 프로그램이 이전 버전에서 실행되고 있으므로 Lombok을 사용하는 것이 그다지 의미가없는 이유를 말씀하셨습니다. 현재 프로젝트에서 우리는 최근 Java 11로 마이그레이션했으며 Lombok을 사용하여 불변 클래스를 달성하고 있습니다. 또한 내 대답은 이미 존재했던 대답에 대한 추가입니다. 불변하는 것은 이미 답변을 받았으며, 완성을 위해 그것을 다시 작성하기를 기대하고 계신 것 같습니다.
Anubhav

그럴 수 있지. 나는 찬성도 반대도하지 않았다. 다른 두 사람이이 답변에 반대표를 던진 이유를 지적하려고했습니다. 답변을 편집할지 여부와 방법은 귀하에게 달려 있습니다.
Ivo Mori
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.