[java] 최종 개체를 수정할 수있는 이유는 무엇입니까?

작업중인 코드베이스에서 다음 코드를 발견했습니다.

public final class ConfigurationService {
    private static final ConfigurationService INSTANCE = new ConfigurationService();
    private List providers;

    private ConfigurationService() {
        providers = new ArrayList();
    }

    public static void addProvider(ConfigurationProvider provider) {
        INSTANCE.providers.add(provider);
    }

    ...

INSTANCE로 선언됩니다 final. 에 개체를 추가 할 수있는 이유는 무엇 INSTANCE입니까? 그것이 final 사용을 무효화해서는 안됩니다. (그렇지 않습니다).

나는 대답이 포인터와 메모리로 무언가를해야한다고 가정하고 있지만 확실히 알고 싶습니다.



답변

final단순히 개체 참조를 변경할 수 없게 만듭니다 . 가리키는 객체는 이렇게해도 불변하지 않습니다. INSTANCE다른 개체를 참조 할 수 없지만 참조하는 개체는 상태를 변경할 수 있습니다.


답변

최종적인 것은 불변하는 것과 다릅니다.

final != immutable

final키워드는 반드시 참조가 변경되지 않습니다를 만드는 데 사용됩니다 (즉,이 새 것으로 대체 할 수있다 참조)

그러나 속성 자체가 수정 가능한 경우 방금 설명한대로 수행해도됩니다.

예를 들어

class SomeHighLevelClass {
    public final MutableObject someFinalObject = new MutableObject();
}

이 클래스를 인스턴스화하면 finalsomeFinalObject 이기 때문에 속성에 다른 값을 할당 할 수 없습니다 .

따라서 이것은 불가능합니다.

....
SomeHighLevelClass someObject = new SomeHighLevelClass();
MutableObject impostor  = new MutableObject();
someObject.someFinal = impostor; // not allowed because someFinal is .. well final

그러나 객체 자체가 다음과 같이 변경 가능한 경우 :

class MutableObject {
     private int n = 0;

     public void incrementNumber() {
         n++;
     }
     public String toString(){
         return ""+n;
     }
}

그런 다음 해당 변경 가능한 객체에 포함 된 값이 변경 될 수 있습니다.

SomeHighLevelClass someObject = new SomeHighLevelClass();

someObject.someFinal.incrementNumber();
someObject.someFinal.incrementNumber();
someObject.someFinal.incrementNumber();

System.out.println( someObject.someFinal ); // prints 3

이것은 귀하의 게시물과 동일한 효과가 있습니다.

public static void addProvider(ConfigurationProvider provider) {
    INSTANCE.providers.add(provider);
}

여기에서 INSTANCE의 값을 변경하지 않고 내부 상태를 수정합니다 (via, provider.add 메소드).

클래스 정의가 다음과 같이 변경되는 것을 방지하려면 :

public final class ConfigurationService {
    private static final ConfigurationService INSTANCE = new ConfigurationService();
    private List providers;

    private ConfigurationService() {
        providers = new ArrayList();
    }
    // Avoid modifications      
    //public static void addProvider(ConfigurationProvider provider) {
    //    INSTANCE.providers.add(provider);
    //}
    // No mutators allowed anymore :) 
....

그러나 그것은별로 의미가 없을 수도 있습니다. 🙂

그건 그렇고, 기본적으로 같은 이유로 액세스를 동기화 해야합니다 .


답변

오해의 열쇠는 질문 제목에 있습니다. 최종적인 것은 객체 가 아니라 변수 입니다. 변수의 값은 변경할 수 없지만 그 안의 데이터는 변경할 수 있습니다.

참조 유형 변수를 선언 할 때 해당 변수의 값은 객체가 아니라 참조라는 점을 항상 기억하십시오.


답변

final 은 참조를 변경할 수 없음을 의미합니다. 최종으로 선언 된 경우 INSTANCE를 다른 참조에 다시 할당 할 수 없습니다. 객체의 내부 상태는 여전히 변경 가능합니다.

final ConfigurationService INSTANCE = new ConfigurationService();
ConfigurationService anotherInstance = new ConfigurationService();
INSTANCE = anotherInstance;

컴파일 오류가 발생합니다.


답변

한 번 final변수가 할당되어, 항상 같은 값을 포함합니다. 경우 final변수가 객체에 대한 참조를 보유하고, 그 개체의 상태가 개체의 동작에 의해 변경 될 수 있지만, 변수가 항상 동일한 객체를 참조한다. 이것은 배열에도 적용됩니다. 배열은 객체이기 때문입니다. 경우 final변수 배열에 대한 참조를 보유하고, 그 배열의 요소는 어레이의 동작에 의해 변경 될 수 있지만, 변수가 항상 동일한 배열을 참조 할 것이다.

출처

다음 은 객체를 변경 불가능하게 만드는 방법 에 대한 가이드입니다 .


답변

최종 및 불변은 같은 것이 아닙니다. 최종은 참조를 재 할당 할 수 없으므로 말할 수 없음을 의미합니다.

INSTANCE = ...

불변은 객체 자체를 수정할 수 없음을 의미합니다. 이것의 예는 java.lang.String클래스입니다. 문자열 값은 수정할 수 없습니다.


답변

Java에는 언어에 내장 된 불변성 개념이 없습니다. 메서드를 mutator로 표시 할 수있는 방법은 없습니다. 따라서 언어는 객체 불변성을 강제 할 방법이 없습니다.