Java에서 불변 객체를 만드는 방법은 무엇입니까?


83

Java에서 불변 객체를 만드는 방법은 무엇입니까?

불변이라고 부르는 객체는 무엇입니까?

모든 정적 멤버가있는 클래스가 있으면 변경할 수 없습니까?



1
위에 링크 된 질문은 동일하지 않지만 해당 질문에 대한 답변은 모든 질문에 답해야합니다.
Joachim Sauer 2011 년

클래스가 모두 정적 멤버 인 경우 상태 비 저장 (인스턴스에 개별 상태가 없음)이며 변경 가능 또는 불변의 문제가 문제가됩니다.
Sebastian Redl 2014

생성자 이외의 필드를 초기화하는 다른 방법이 있습니까? 수업에 20 개 이상의 필드가 있습니다. 생성자를 사용하여 모든 필드를 초기화하는 것은 매우 어렵고 일부 필드도 선택 사항입니다.
Nikhil Mishra

답변:


88

다음은 불변 객체 의 엄격한 요구 사항입니다.

  1. 수업 최종 결정
  2. 모든 멤버를 최종적으로 만들고, 명시 적으로, 정적 블록 또는 생성자에서 설정합니다.
  3. 모든 구성원을 비공개로 설정
  4. 상태를 수정하는 메소드 없음
  5. 변경 가능한 멤버에 대한 액세스를 제한하는 데 매우주의해야합니다 (필드는있을 수 final있지만 객체는 여전히 변경 가능합니다. 즉 private final Date imStillMutable). defensive copies이러한 경우에 만들어야 합니다.

수업을 만드는 이유 final는 매우 미묘하고 종종 간과됩니다. 최종 사용자가 아닌 경우 자유롭게 클래스를 확장하고, 재정의 public또는 protected동작을 수행하고, 변경 가능한 속성을 추가하고, 해당 하위 클래스를 대체로 제공 할 수 있습니다. 클래스 final를 선언함으로써 이런 일이 발생하지 않도록 할 수 있습니다.

실제 문제를 보려면 아래 예를 고려하십시오.

public class MyApp{

    /**
     * @param args
     */
    public static void main(String[] args){

        System.out.println("Hello World!");

        OhNoMutable mutable = new OhNoMutable(1, 2);
        ImSoImmutable immutable = mutable;

        /*
         * Ahhhh Prints out 3 just like I always wanted
         * and I can rely on this super immutable class 
         * never changing. So its thread safe and perfect
         */
        System.out.println(immutable.add());

        /* Some sneak programmer changes a mutable field on the subclass */
        mutable.field3=4;

        /*
         * Ahhh let me just print my immutable 
         * reference again because I can trust it 
         * so much.
         * 
         */
        System.out.println(immutable.add());

        /* Why is this buggy piece of crap printing 7 and not 3
           It couldn't have changed its IMMUTABLE!!!! 
         */
    }

}

/* This class adheres to all the principles of 
*  good immutable classes. All the members are private final
*  the add() method doesn't modify any state. This class is 
*  just a thing of beauty. Its only missing one thing
*  I didn't declare the class final. Let the chaos ensue
*/ 
public class ImSoImmutable{
    private final int field1;
    private final int field2;

    public ImSoImmutable(int field1, int field2){
        this.field1 = field1;
        this.field2 = field2;
    }

    public int add(){
        return field1+field2;
    }
}

/*
This class is the problem. The problem is the 
overridden method add(). Because it uses a mutable 
member it means that I can't  guarantee that all instances
of ImSoImmutable are actually immutable.
*/ 
public class OhNoMutable extends ImSoImmutable{   

    public int field3 = 0;

    public OhNoMutable(int field1, int field2){
        super(field1, field2);          
    }

    public int add(){
       return super.add()+field3;  
    }

}

실제로 Dependency Injection 환경에서 위의 문제가 발생하는 것은 매우 일반적입니다. 당신은 명시 적으로 인스턴스화하지 않으며 주어진 수퍼 클래스 참조는 실제로 하위 클래스 일 수 있습니다.

중요한 점은 불변성에 대한 확실한 보장을하려면 클래스를 final. 이것은 Joshua Bloch의 Effective Java 에서 자세히 다루고 Java 메모리 모델 사양에서 명시 적으로 참조 됩니다.


모든 정적 멤버는 어떻습니까?
Neel Salpe 2011-06-10

1
수업이 최종일 필요는 없습니다.
Angel O'Sphere 2011-06-10

12
@Nilesh : 불변성은 인스턴스 의 속성입니다 . 정적 멤버는 일반적으로 단일 인스턴스와 관련이 없으므로 여기에 표시되지 않습니다.
Joachim Sauer 2011 년

4
불변성에 관한 Joshua Bloch의 항목 15-상태를 수정하는 메소드 없음, 모든 필드 최종, 모든 필드 개인, 클래스 확장 불가 확인, 변경 가능한 구성 요소에 대한 독점 액세스 보장.
nsfyn55 2011-06-10

2
@Jaochim-그것들은 절대적으로 방정식의 일부입니다. 위의 예를 들어 변경 가능한 정적 멤버를 추가하고 ImSoImmutable의 추가 함수에서 사용하면 동일한 문제가 발생합니다. 클래스가 불변이면 모든 측면은 불변이어야합니다.
nsfyn55 2011-06-10

14

클래스에 공개 뮤 테이터 (세터) 메서드를 추가하지 마십시오.


모든 정적 멤버는 어떻습니까? 이러한 유형의 객체에 대해 객체의 참조 또는 상태가 변경됩니까?
Neel Salpe 2011-06-10

7
상관 없습니다. 어떤 방법으로 외부에서 변경할 수없는 경우 변경할 수 없습니다.
BalusC 2011 년

정적 인 구성원이 무엇을하는지 모르기 때문에 대답 할 수 없습니다. 그렇게한다면 그 클래스는 불변하지 않습니다.
Angel O'Sphere 2011-06-10

그리고 클래스 기본 생성자는 private이거나이어야합니다 final. 상속을 피하기 위해. 상속은 캡슐화를 위반하기 때문입니다.
Talha Ahmed Khan 2011

변경 가능한 객체 (예 : List)를 변경 불가능한 객체로 전달한 다음 외부에서 변경하는 것은 어떻습니까? 이것은 가능하며 객체 생성 중에 방어 복사본을 사용하여 처리해야합니다
Yassin Hajaj

14

클래스는 불변이 아니며 객체는 불변입니다.

불변의 의미 : 내 공개 표시 상태는 초기화 후에 변경할 수 없습니다.

필드를 최종적으로 선언 할 필요는 없지만 스레드 안전성을 보장하는 데 큰 도움이 될 수 있습니다.

클래스에 정적 멤버 만있는 경우 해당 개체의 상태를 변경할 수 없기 때문에이 클래스의 개체는 변경할 수 없습니다 (아마도 만들 수 없습니다. :)).


3
모든 필드를 정적으로 만드는 것은 실제로 유용하지 않은 동일한 상태를 공유하도록 모든 의도를 제한합니다.
aviad

6

Java에서 클래스를 변경 불가능하게 만들려면 다음 사항에 유의하십시오.

1. 클래스의 인스턴스 변수 값을 수정하는 setter 메소드를 제공하지 마십시오.

2. 클래스를 'final' 로 선언합니다 . 이것은 다른 클래스가 그것을 확장하는 것을 막아서 인스턴스 변수 값을 수정할 수있는 메소드를 재정의하는 것을 방지합니다.

3. 인스턴스 변수를 private 및 final 로 선언합니다 .

4. 클래스의 생성자를 전용 으로 선언하고 필요할 때 클래스의 인스턴스를 생성하는 팩토리 메서드를 추가 할 수도 있습니다 .

이 점이 도움이 될 것입니다 !!


3
WRT # 4 생성자 가시성은 가변성에 어떤 영향을 줍니까? 문자열은 변경할 수 없지만 여러 공용 생성자가 있습니다.
라이언

@Ryan이 말한 것처럼 인스턴스 변수에도 동일하게 적용됩니다. 왜 선언해야 private합니까?
MC Emperor

이 답변에 찬성표가있는 이유가 확실하지 않습니다. 이 답변은 불완전합니다. 다루어야 할 중요한 변경 가능한 객체에 대해서는 언급하지 않습니다. 더 나은 이해를 위해 @ nsfyn55의 설명을 읽으십시오.
Ketan R

4

에서 오라클 사이트, 어떻게 자바에서 불변의 객체를 만들 수 있습니다.

  1. 필드가 참조하는 필드 또는 개체를 수정하는 "세터"메서드를 제공하지 마십시오.
  2. 모든 필드를 최종 및 비공개로 설정합니다.
  3. 하위 클래스가 메서드를 재정의하도록 허용하지 마십시오. 이를 수행하는 가장 간단한 방법은 클래스를 final로 선언하는 것입니다. 보다 정교한 접근 방식은 생성자를 비공개로 만들고 팩토리 메서드에서 인스턴스를 생성하는 것입니다.
  4. 인스턴스 필드에 변경 가능한 개체에 대한 참조가 포함 된 경우 해당 개체의 변경을 허용
    하지 마십시오 . I. 변경 가능한 개체를 수정하는 메서드를 제공하지 마십시오.
    II. 변경 가능한 객체에 대한 참조를 공유하지 마십시오. 생성자에 전달 된 변경 가능한 외부 객체에 대한 참조를 저장하지 마십시오. 필요한 경우 복사본을 만들고 복사본에 대한 참조를 저장합니다. 마찬가지로 메서드에서 원본을 반환하지 않도록 필요한 경우 내부 변경 가능 개체의 복사본을 만듭니다.

3

불변 객체는 생성 후 내부 상태를 변경하지 않는 객체입니다. 동기화없이 스레드간에 공유 할 수 있기 때문에 다중 스레드 응용 프로그램에서 매우 유용 합니다.

불변 객체를 만들려면 몇 가지 간단한 규칙을 따라야합니다.

1. setter 메서드를 추가하지 마십시오.

불변 객체를 빌드하는 경우 내부 상태는 변경되지 않습니다. setter 메서드의 작업은 필드의 내부 값을 변경하여 추가 할 수 없도록하는 것입니다.

2. 모든 필드를 최종 및 비공개로 선언

비공개 필드는 클래스 외부에서 볼 수 없으므로 수동 변경 사항을 적용 할 수 없습니다.

최종 필드를 선언하면 기본 값을 참조하는 경우 개체를 참조하는 경우 값이 변경되지 않고 참조를 변경할 수 없습니다. 이것은 private final 필드 만있는 객체가 변경되지 않도록하기에는 충분하지 않습니다.

3. 필드가 변경 가능한 객체 인 경우 getter 메서드에 대한 방어 복사본을 만듭니다.

우리는 내부 상태를 변경할 수 있기 때문에 필드 final 및 private을 정의하는 것으로 충분하지 않다는 것을 전에 보았습니다. 이 문제를 해결하려면 해당 필드의 방어적인 복사본을 만들고 요청 될 때마다 해당 필드를 반환해야합니다.

4. 생성자에 전달 된 변경 가능한 개체가 필드에 할당되어야하는 경우 방어적인 복사본을 만듭니다.

변경이 가능하기 때문에 생성자에 전달 된 참조를 보유하는 경우에도 동일한 문제가 발생합니다. 따라서 생성자에 전달 된 객체에 대한 참조를 보유하면 변경 가능한 객체를 만들 수 있습니다. 이 문제를 해결하려면 매개 변수가 변경 가능한 객체 인 경우 방어적인 매개 변수 사본을 만들어야합니다.

참고 필드는 불변 오브젝트에 대한 참조인지 생성자하고 최종적으로 전용 필드를 정의하기에 충분 게터 방법에 운전자의 복사본을 생성 할 필요가 없다.

5. 하위 클래스가 메서드를 재정의하도록 허용하지 마십시오.

하위 클래스가 메서드를 재정의하면 방어적인 복사본 대신 변경 가능한 필드의 원래 값을 반환 할 수 있습니다.

이 문제를 해결하려면 다음 중 하나를 수행 할 수 있습니다.

  1. 불변 클래스를 final로 선언하여 확장 할 수 없습니다.
  2. 변경할 수없는 클래스의 모든 메서드를 final로 선언하여 재정의 할 수 없도록합니다.
  3. 개인 생성자가있는 클래스는 확장 할 수 없으므로 변경 불가능한 클래스의 인스턴스를 만들기 위해 개인 생성자와 팩토리를 만듭니다.

이러한 간단한 규칙을 따르면 스레드로부터 안전하므로 스레드간에 변경 불가능한 객체를 자유롭게 공유 할 수 있습니다!

다음은 몇 가지 주목할만한 사항입니다.

  • 불변 객체는 실제로 많은 경우 삶을 더 단순하게 만듭니다. 특히 개체에 ID가 없어서 쉽게 교체 할 수 있고 동시 프로그래밍 방식을 더 안전하고 깔끔하게 만들 수있는 값 유형에 특히 적용 할 수 있습니다 (동시성 버그를 찾기 어려운 것으로 악명 높은 대부분은 궁극적으로 서로 공유되는 가변 상태로 인해 발생합니다. 스레드). 그러나 크고 / 또는 복잡한 개체 의 경우 모든 변경 사항에 대해 개체의 새 복사본을 만드는 것은 비용이 많이 들거나 지루할 수 있습니다. 있습니다. 고유 한 ID를 가진 개체의 경우 기존 개체를 변경하는 것이 새로 수정 된 복사본을 만드는 것보다 훨씬 간단하고 직관적입니다.
  • 양방향 관계 와 같이 변경 불가능한 객체로는 할 수없는 일이 있습니다 . 한 개체에 연결 값을 설정하면 ID가 변경됩니다. 따라서 다른 개체에 새 값을 설정하면 변경됩니다. 문제는 첫 번째 개체의 참조가 더 이상 유효하지 않다는 것입니다. 참조가있는 개체를 나타내는 새 인스턴스가 생성 되었기 때문입니다. 계속하면 무한 회귀가 발생합니다.
  • 이진 검색 트리 를 구현하려면 매번 새 트리를 반환해야합니다. 새 트리는 수정 된 각 노드의 복사본을 만들어야합니다 (수정되지 않은 분기는 공유 됨). 삽입 기능의 경우 이것은 그리 나쁘지 않지만 삭제 및 재조정 작업을 시작했을 때 일이 빠르게 비효율적이었습니다.
  • Hibernate와 JPA는 본질적으로 시스템이 변경 가능한 객체를 사용하도록 지시합니다. 그 이유는 전체 전제가 데이터 객체의 변경 사항을 감지하고 저장하기 때문입니다.
  • 언어에 따라 컴파일러는 변경 불가능한 데이터를 처리 할 때 데이터가 절대 변경되지 않는다는 것을 알고 있기 때문에 많은 최적화를 수행 할 수 있습니다. 모든 종류의 항목을 건너 뛰므로 엄청난 성능 이점을 얻을 수 있습니다.
  • 다른 알려진 JVM 언어 ( Scala, Clojure ) 를 살펴보면 변경 가능한 객체가 코드에서 거의 보이지 않으므로 사람들이 단일 스레딩으로 충분하지 않은 시나리오에서 사용하기 시작합니다.

옳고 그름은 없으며 당신이 선호하는 것에 달려 있습니다. 그것은 당신의 선호도와 당신이 성취하고자하는 것에 달려 있습니다 (그리고 어느 한쪽의 열렬한 팬들을 소외시키지 않고 두 접근법을 쉽게 사용할 수 있다는 것은 일부 언어가 추구하는 성배입니다).


2
  • 필드가 참조하는 필드 또는 개체를 수정하는 "세터"메서드를 제공하지 마십시오.
  • 모든 필드를 최종 및 비공개로 설정합니다.
  • 하위 클래스가 메서드를 재정의하도록 허용하지 마십시오. 이를 수행하는 가장 간단한 방법은 클래스를 final로 선언하는 것입니다. 보다 정교한 접근 방식은 생성자를 비공개로 만들고 팩토리 메서드에서 인스턴스를 생성하는 것입니다.
  • 인스턴스 필드에 변경 가능한 개체에 대한 참조가 포함되어있는 경우 해당 개체를 변경할 수 없습니다.
    • 변경 가능한 개체를 수정하는 메서드를 제공하지 마십시오.
    • 변경 가능한 객체에 대한 참조를 공유하지 마십시오. 생성자에 전달 된 변경 가능한 외부 객체에 대한 참조를 저장하지 마십시오. 필요한 경우 복사본을 만들고 복사본에 대한 참조를 저장합니다. 마찬가지로 메서드에서 원본을 반환하지 않도록 필요한 경우 내부 변경 가능 개체의 복사본을 만듭니다.

2

우선 불변 객체를 만들어야하는 이유와 불변 객체의 장점은 무엇인지 알고 있습니다.

불변 객체의 장점

동시성 및 멀티 스레딩 자동으로 스레드로부터 안전하므로 동기화 문제가 발생합니다 .... etc

생성자복사 할 필요가 없습니다 . 복제 를 구현할 필요가 없습니다 . 클래스를 재정의 할 수 없음 필드를 비공개 및 최종 강제 호출자가 인수없는 생성자를 사용하는 대신 단일 단계에서 개체를 완전히 생성하도록합니다.

불변 객체는 단순히 상태가 불변 객체가 생성 된 후에 객체의 데이터를 변경할 수 없음을 의미하는 객체입니다.

아래 코드를 참조하십시오.

public final class ImmutableReminder{
    private final Date remindingDate;

    public ImmutableReminder (Date remindingDate) {
        if(remindingDate.getTime() < System.currentTimeMillis()){
            throw new IllegalArgumentException("Can not set reminder" +
                    " for past time: " + remindingDate);
        }
        this.remindingDate = new Date(remindingDate.getTime());
    }

    public Date getRemindingDate() {
        return (Date) remindingDate.clone();
    }
}

2

가변성 최소화

불변 클래스는 단순히 인스턴스를 수정할 수없는 클래스입니다. 각 인스턴스에 포함 된 모든 정보는 생성시 제공되며 개체의 수명 동안 고정됩니다.

JDK 불변 클래스 : 문자열, 박스형 기본 클래스 (래퍼 클래스), BigInteger 및 BigDecimal 등

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

  1. 개체의 상태를 수정하는 메서드 (뮤 테이터라고 함)를 제공하지 마세요.
  2. 클래스를 확장 할 수 없는지 확인하십시오.
  3. 모든 필드를 최종적으로 만드십시오.
  4. 모든 필드를 비공개로 설정합니다. 이렇게하면 클라이언트가 필드에서 참조하는 변경 가능한 개체에 대한 액세스 권한을 얻고 이러한 개체를 직접 수정할 수 없습니다.
  5. 방어적인 사본을 만드십시오. 변경 가능한 구성 요소에 대한 독점적 인 액세스를 보장합니다.

    public List getList () {return Collections.unmodifiableList (list); <=== 호출자에게 반환하기 전에 변경 가능한 필드의 방어 복사본}

클래스에 변경 가능한 객체를 참조하는 필드가있는 경우 클래스의 클라이언트가 이러한 객체에 대한 참조를 얻을 수 없는지 확인하십시오. 이러한 필드를 클라이언트 제공 개체 참조로 초기화하거나 접근 자로부터 개체 참조를 반환하지 마십시오.

import java.util.Date;
public final class ImmutableClass {

       public ImmutableClass(int id, String name, Date doj) {
              this.id = id;
              this.name = name;
              this.doj = doj;
       }

       private final int id;
       private final String name;
       private final Date doj;

       public int getId() {
              return id;
       }
       public String getName() {
              return name;
       }

     /**
      * Date class is mutable so we need a little care here.
      * We should not return the reference of original instance variable.
      * Instead a new Date object, with content copied to it, should be returned.
      * */
       public Date getDoj() {
              return new Date(doj.getTime()); // For mutable fields
       }
}
import java.util.Date;
public class TestImmutable {
       public static void main(String[] args) {
              String name = "raj";
              int id = 1;
              Date doj = new Date();

              ImmutableClass class1 = new ImmutableClass(id, name, doj);
              ImmutableClass class2 = new ImmutableClass(id, name, doj);
      // every time will get a new reference for same object. Modification in              reference will not affect the immutability because it is temporary reference.
              Date date = class1.getDoj();
              date.setTime(date.getTime()+122435);
              System.out.println(class1.getDoj()==class2.getDoj());
       }
}

자세한 내용은 내 블로그를 참조하십시오.
http://javaexplorer03.blogspot.in/2015/07/minimize-mutability.html


@Pang은 생성자 이외의 필드를 초기화하는 다른 방법이 있습니다. 수업에 20 개 이상의 필드가 있습니다. 생성자를 사용하여 모든 필드를 초기화하는 것은 매우 어렵고 일부 필드도 선택 사항입니다.
Nikhil Mishra 2018

1
@NikhilMishra, 빌더 디자인 패턴을 사용하여 객체 생성 중에 변수를 초기화 할 수 있습니다. 생성자에서 설정할 필수 변수를 유지하고 setter 메서드를 사용하여 설정할 나머지 선택적 변수를 유지할 수 있습니다. 그러나 엄밀히 말하면 True Immutable 클래스를 만들지 않을 것입니다.
sunny_dev

1

객체가 생성 된 후 상태를 변경할 수없는 경우 불변이라고합니다. Java에서 변경 불가능한 클래스를 생성하는 가장 간단한 방법 중 하나는 모든 필드를 final로 설정하는 것입니다. "java.util.Date"와 같은 변경 가능한 클래스를 포함하는 변경 불가능한 클래스를 작성해야하는 경우. 이러한 경우 불변성을 보존하기 위해 원본 객체의 사본을 반환하는 것이 좋습니다.


사본을 반환하는 것이 아니라 필요합니다. 그러나 생성자에서 방어적인 복사본을 만들어야합니다. 그렇지 않으면 개체가 장면 뒤에서 변경 될 수 있습니다.
라이언

1

불변 객체는 일단 생성되면 상태를 변경할 수없는 객체입니다. 예를 들어 String 클래스는 불변 클래스입니다. 변경 불가능한 개체는 수정할 수 없으므로 동시 실행에서도 스레드로부터 안전합니다.

불변 클래스의 특징 :

  • 구성이 간단
  • 자동 스레드 안전
  • 처리하는 동안 내부 상태가 변경되지 않으므로 Map 키 및 Set에 적합한 후보
  • 항상 동일한 상태를 나타내므로 복제 구현이 필요하지 않습니다.

불변 클래스를 작성하기위한 키 :

  • 클래스를 재정의 할 수 없는지 확인하십시오.
  • 모든 멤버 변수를 비공개 및 최종화
  • 세터 메서드를 제공하지 마십시오
  • 구성 단계에서 개체 참조가 유출되면 안됩니다.

1

클래스를 불변 클래스로 원할 때 다음 몇 단계를 고려해야합니다.

  1. 수업은 최종으로 표시되어야합니다.
  2. 모든 필드는 비공개 및 최종 필드 여야합니다.
  3. setter를 생성자로 대체하십시오 (변수에 값을 할당하기 위해).

위에서 입력 한 내용을 살펴 보겠습니다.

//ImmutableClass
package younus.attari;

public final class ImmutableExample {

    private final String name;
    private final String address;

    public ImmutableExample(String name,String address){
        this.name=name;
        this.address=address;
    }


    public String getName() {
        return name;
    }

    public String getAddress() {
        return address;
    }

}

//MainClass from where an ImmutableClass will be called
package younus.attari;

public class MainClass {

    public static void main(String[] args) {
        ImmutableExample example=new ImmutableExample("Muhammed", "Hyderabad");
        System.out.println(example.getName());

    }
}

0

일반적으로 무시되지만 변경 불가능한 객체의 중요한 속성

@ nsfyn55에서 제공하는 답변까지 추가, 다음과 같은 측면은 또한 가능한 객체 불변성, 고려 될 필요가 주요 중요성

다음 클래스를 고려하십시오.

public final class ImmutableClass {

  private final MutableClass mc;

  public ImmutableClass(MutableClass mc) {
    this.mc = mc;
  }

  public MutableClass getMutClass() {
    return this.mc;
  }
}

public class MutableClass {

  private String name;

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

  public void setName(String name) {
    this.name = name;
  }
}


public class MutabilityCheck {

public static void main(String[] args) {

  MutableClass mc = new MutableClass();

  mc.setName("Foo");

  ImmutableClass iMC = new ImmutableClass(mc);

  System.out.println(iMC.getMutClass().getName());

  mc.setName("Bar");

  System.out.println(iMC.getMutClass().getName());

  }

 }

다음은 MutabilityCheck의 출력입니다.

 Foo
 Bar

다음 사항에 유의하는 것이 중요합니다.

  1. 다음 변경 사항에 의해 설명 된 불변의 인스턴스 변수 에 '복사' 또는 '클로 잉' 하여 불변 객체 (constructor를 통해)에 변경 가능한 객체를 생성 합니다.

    public final class ImmutableClass {
    
       private final MutableClass mc;
    
       public ImmutableClass(MutableClass mc) {
         this.mc = new MutableClass(mc);
       }
    
       public MutableClass getMutClass() {
         return this.mc;
       }
    
     }
    
     public class MutableClass {
    
      private String name;
    
      public MutableClass() {
    
      }
      //copy constructor
      public MutableClass(MutableClass mc) {
        this.name = mc.getName();
      }
    
      public String getName() {
        return this.name;
      }
    
      public void setName(String name) {
       this.name = name;
      } 
     }
    

다음이 MutabilityCheck 클래스에서 여전히 유효하기 때문에 여전히 완전한 불변성을 보장하지 않습니다.

  iMC.getMutClass().setName("Blaa");
  1. 그러나 1에서 변경 한 내용으로 MutabilityCheck를 실행하면 다음과 같은 결과가 출력됩니다.

    Foo
    Foo
    
  2. 객체에 대한 완전한 불변성을 달성하려면 모든 종속 객체도 불변이어야합니다.


0

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");
  }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.