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


89

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

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の使用を無効にするべきではありません。(そうではありません)。

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


この誤解は、必ずしも問題としてではなく、かなり頻繁に発生します。多くの場合、回答またはコメントとして。
ロビン

5
JLSからの簡単な説明:「最終変数がオブジェクトへの参照を保持している場合、オブジェクトの状態はオブジェクトの操作によって変更される可能性がありますが、変数は常に同じオブジェクトを参照します。」 JLSドキュメント
realPK 2016年

回答:


161

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


1
+1、詳細については、java.sun.com / docs / books / jls / second_edition / html /…、セクション4.5.4を確認してください。
アベルモレロス2010年

それでConfigurationService、私がオブジェクトを逆シリアル化し、インスタンスを実行しようとしたとしましょう=deserializedConfigurationService許可されませんか?
diegoaguilar 2013年

INSTANCE別のオブジェクトを参照するように割り当てることはできません。他のオブジェクトがどこから来たのかは関係ありません。(注:このクラスをロードしたのは1INSTANCEClassLoaderです。理論的には1つのJVMにクラスを複数回ロードでき、それぞれが別々です。ただし、これは異なる技術的なポイントです。)
Sean Owen

@AkhilGite私の答えに対するあなたの編集はそれを間違ったものにしました。それは実際に文の意味を逆転させました、それは正しかったです。参照は不変です。オブジェクトは変更可能です。「不変になる」ことはありません。
ショーンオーウェン

@SeanOwen Sry、私が編集したことに対して、あなたの発言は完全に正しいです、そしてありがとう。
akhilGite 2017年

33

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

final != immutable

finalキーワードは必ず参照が変更されませんを作るために使用されている(であること、それは新しいもので置換することはできませんしている参照)

ただし、属性が自己変更可能である場合は、今説明したことを実行しても問題ありません。

例えば

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

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

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

....
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 :) 
....

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

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


26

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

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


11

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

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

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


7

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

ソース

これは、オブジェクトを不変するためのガイドです。


4

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

INSTANCE = ...

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


2

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.