web-dev-qa-db-ja.com

最終オブジェクトを変更できるのはなぜですか?

私が取り組んでいるコードベースで次のコードに出会いました:

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

    ...

INSTANCEfinalとして宣言されます。オブジェクトをINSTANCEに追加できるのはなぜですか?それはfinalの使用を無効にすべきではありません。 (そうではありません)。

私は答えがポインタとメモリで何かをしなければならないが、確かに知りたいと思っています。

80
Matt McCormick

finalは、単にオブジェクトを変更します参照変更不可。これが指すオブジェクトは、これを行うことで不変ではありません。 INSTANCEは別のオブジェクトを参照することはできませんが、それが参照するオブジェクトは状態を変更する場合があります。

151
Sean Owen

最終的であることは、不変であることと同じではありません。

final != immutable

finalキーワードは、参照が変更されていないことを確認するために使用されます(つまり、参照が持っている参照を新しいものに置き換えることはできません)

ただし、属性がselfで変更可能な場合は、今説明したことを実行してもかまいません。

例えば

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

このクラスをインスタンス化すると、finalであるため、属性someFinalObjectに他の値を割り当てることができなくなります。

したがって、これは不可能です。

....
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の値を変更するのではなく、その内部状態を変更しています(providers.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 :) 
....

しかし、それはあまり意味をなさないかもしれません:)

ちなみに、基本的に同じ理由で アクセスへの同期 もする必要があります。

33
OscarRyz

誤解の鍵は、質問のタイトルにあります。最終的なのはobjectではなく、variableです。変数の値は変更できませんが、変数内のデータは変更できます。

参照型の変数を宣言するとき、その変数の値はオブジェクトではなく参照であることを常に覚えておいてください。

24
Jon Skeet

最終は、参照を変更できないことを意味します。 INSTANCEが最終として宣言されている場合、INSTANCEを別の参照に再割り当てすることはできません。オブジェクトの内部状態はまだ変更可能です。

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

コンパイルエラーがスローされます

11
Theo

final変数が割り当てられると、常に同じ値が含まれます。 final変数がオブジェクトへの参照を保持する場合、オブジェクトの操作によってオブジェクトの状態が変更される場合がありますが、変数は常に同じオブジェクトを参照します。配列はオブジェクトであるため、これは配列にも適用されます。 final変数が配列への参照を保持している場合、配列の操作によって配列のコンポーネントが変更される場合がありますが、変数は常に同じ配列を参照します。

ソース

オブジェクトを不変にする に関するガイドを次に示します。

6
defectivehalt

Finalとimmutableは同じものではありません。最後は、参照を再割り当てできないため、言うことはできないことを意味します

INSTANCE = ...

不変とは、オブジェクト自体を変更できないことを意味します。この例は、Java.lang.Stringクラス。文字列の値を変更することはできません。

4
Chris Dail

Javaには、言語に組み込まれた不変性の概念がありません。メソッドをミューテーターとしてマークする方法はありません。したがって、言語にはオブジェクトの不変性を強制する方法がありません。

2
Steve Kuo