Programing

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

lottogame 2020. 10. 8. 07:40
반응형

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


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

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

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


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

  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 메모리 모델 사양에서 명시 적으로 참조 됩니다.


클래스에 public mutator (setter) 메소드를 추가하지 마십시오.


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

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

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

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


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

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

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

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

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

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


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

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

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

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

불변 객체의 장점

동시성 및 멀티 스레딩 자동으로 스레드로부터 안전하므로 동기화 문제가 발생합니다 .... 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();
    }
}

가변성 최소화

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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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


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

불변 클래스의 특징 :

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

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

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

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

  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());

    }
}

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

@ 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. Constructing mutable objects on an immutable object ( through the constructor ), either by 'copying' or 'cloing' to instance variables of the immutable described by the following changes:

    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;
      } 
     }
    

still does not ensure complete immutability since the following is still valid from the class MutabilityCheck:

  iMC.getMutClass().setName("Blaa");
  1. However, running MutabilityCheck with the changes made in 1. will result in the output being:

    Foo
    Foo
    
  2. In order to achieve complete immutability on an object, all its dependent objects must also be immutable

참고URL : https://stackoverflow.com/questions/6305752/how-to-create-immutable-objects-in-java

반응형