私が取り組んでいるコードベースで次のコードに出会いました:
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
キーワードは、参照が変更されていないことを確認するために使用されます(つまり、参照が持っている参照を新しいものに置き換えることはできません)
ただし、属性が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 :)
....
しかし、それはあまり意味をなさないかもしれません:)
ちなみに、基本的に同じ理由で アクセスへの同期 もする必要があります。
誤解の鍵は、質問のタイトルにあります。最終的なのはobjectではなく、variableです。変数の値は変更できませんが、変数内のデータは変更できます。
参照型の変数を宣言するとき、その変数の値はオブジェクトではなく参照であることを常に覚えておいてください。
最終は、参照を変更できないことを意味します。 INSTANCEが最終として宣言されている場合、INSTANCEを別の参照に再割り当てすることはできません。オブジェクトの内部状態はまだ変更可能です。
final ConfigurationService INSTANCE = new ConfigurationService();
ConfigurationService anotherInstance = new ConfigurationService();
INSTANCE = anotherInstance;
コンパイルエラーがスローされます
final
変数が割り当てられると、常に同じ値が含まれます。final
変数がオブジェクトへの参照を保持する場合、オブジェクトの操作によってオブジェクトの状態が変更される場合がありますが、変数は常に同じオブジェクトを参照します。配列はオブジェクトであるため、これは配列にも適用されます。final
変数が配列への参照を保持している場合、配列の操作によって配列のコンポーネントが変更される場合がありますが、変数は常に同じ配列を参照します。
オブジェクトを不変にする に関するガイドを次に示します。
Finalとimmutableは同じものではありません。最後は、参照を再割り当てできないため、言うことはできないことを意味します
INSTANCE = ...
不変とは、オブジェクト自体を変更できないことを意味します。この例は、Java.lang.String
クラス。文字列の値を変更することはできません。
Javaには、言語に組み込まれた不変性の概念がありません。メソッドをミューテーターとしてマークする方法はありません。したがって、言語にはオブジェクトの不変性を強制する方法がありません。