さまざまなメンバー変数を持つ1つのクラスがあります。コンストラクターがあり、getterメソッドがありますが、setterメソッドはありません。実際、このオブジェクトは不変でなければなりません。
_public class Example {
private ArrayList<String> list;
}
_
さて、次のことに気づきました。getterメソッドで変数リストを取得すると、新しい値を追加することができます-ArrayList
を変更できます。この変数に対して次回get()
を呼び出すと、変更されたArrayList
が返されます。どうすればいいの?二度と設定しなかったので、作業しました! String
では、この動作は不可能です。ここでの違いは何ですか?
参照が不変だからといって、リストそれが参照するが不変であるとは限りません。
list
が作成された場合でも、final
は許可されます
// changing the object which list refers to
example.getList().add("stuff");
しかし、これはnotを許可します:
// changing list
example.list = new ArrayList<String>(); // assuming list is public
リストを不変にする(最初の行も防ぐ)には、 Collections.unmodifiableList
:
public class Example {
final private ArrayList<String> list;
Example(ArrayList<String> listArg) {
list = Collections.unmodifiableList(listArg);
}
}
(これにより、リストの変更不可能なビューが作成されることに注意してください。誰かが元の参照を保持している場合でも、リストを使用してリストを変更できます。)
文字列では、この動作は不可能です。では、ここの違いは何ですか?
これは、リストをunmodifiableListにした場合と同様に、String
はすでに不変(変更不可)であるためです。
比較:
String data structure | List data structure
.-------------------------+------------------------------------.
Immutable | String | Collection.unmodifiableList(...) |
-----------+-------------------------+------------------------------------|
Mutable | StringBuffer | ArrayList |
'-------------------------+------------------------------------'
list
への参照を返しています。また、list
は不変ではありません。
リストを変更したくない場合は、そのコピーを返します。
public List<String> get() {
return new ArrayList<String>(this.list);
}
または 変更不可能なリスト を返すことができます:
public List<String> get() {
return Collections.unmodifiableList(this.list);
}
重要なのは、stringを変更していないことを理解することです-どの文字列がリストを参照しているかを変更していますcontains。
別の言い方をすれば、もし私があなたの財布から1ドルを取り出し、それを10セント硬貨に交換した場合、1ドルも10セントも変更していません-私はあなたの財布の内容を変更しました。
リストに読み取り専用のビューが必要な場合は、Collections.unmodifiableList
。 wrappingであるリストが変更されるのを防ぐことはできませんが、変更不可能なリストへの参照しか持たないユーザーは、内容。
truly不変リストについては、 Guava の ImmutableList クラスを参照してください。
他の答えが言うように、ゲッターから返すオブジェクトはまだ変更可能です。
コレクションクラスを使用して装飾することで、リストを不変にすることができます。
list = Collections.unmodifiableList(list);
これをクライアントに返すと、クライアントは要素を追加または削除できなくなります。ただし、リストから要素を取得することはできます。そのため、要素が不変であることを確認する必要があります。
Collections.unmodifiableList()は、リストを変更不可能にします。これにより、新しい最終的な配列リストが再び作成され、追加、削除、追加、およびクリアのメソッドがオーバーライドされて、サポートされない操作の例外がスローされます。コレクションクラスのハックです。しかし、コンパイル時に、要素の追加や削除を妨げるものではありません。私はむしろそのリストのクローンを作りたいと思います。これは、既存のオブジェクトを不変に保つのに役立ち、新しいリストの作成にコストがかかりません。複製と新しい演算子の違いを読んでください( http://www.javatpoint.com/object-cloning )。また、実行時にコードがクラッシュするのを防ぎます。
本当に不変のリストを取得するには、リストの内容の詳細なコピーを作成する必要があります。 UnmodifiableListは、参照のリストをいくぶん不変にするだけです。リストまたは配列の深いコピーを作成することは、サイズが大きくなるにつれてメモリ上で困難になります。シリアライゼーション/デシリアライゼーションを利用して、配列/リストの深いコピーを一時ファイルに保存できます。メンバー変数は不変である必要があるため、セッターは使用できません。ゲッターは、メンバー変数をファイルにシリアル化してから、それを非シリアル化してディープコピーを取得します。シリアライゼーションには、オブジェクトツリーの奥深くに行くという生来の性質があります。これにより、パフォーマンスコストをかけた場合でも完全な不変性が保証されます。
package com.home.immutable.serial;
import Java.io.File;
import Java.io.FileInputStream;
import Java.io.FileNotFoundException;
import Java.io.FileOutputStream;
import Java.io.IOException;
import Java.io.ObjectInputStream;
import Java.io.ObjectOutputStream;
public final class ImmutableBySerial {
private final int num;
private final String str;
private final TestObjSerial[] arr;
ImmutableBySerial(int num, String str, TestObjSerial[] arr){
this.num = num;
this.str = str;
this.arr = getDeepCloned(arr);
}
public int getNum(){
return num;
}
public String getStr(){
return str;
}
public TestObjSerial[] getArr(){
return getDeepCloned(arr);
}
private TestObjSerial[] getDeepCloned(TestObjSerial[] arr){
FileOutputStream fos = null;
ObjectOutputStream oos = null;
FileInputStream fis = null;
ObjectInputStream ois = null;
TestObjSerial[] clonedObj = null;
try {
fos = new FileOutputStream(new File("temp"));
oos = new ObjectOutputStream(fos);
oos.writeObject(arr);
fis = new FileInputStream(new File("temp"));
ois = new ObjectInputStream(fis);
clonedObj = (TestObjSerial[])ois.readObject();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
try {
oos.close();
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return clonedObj;
}
}
したがって、リストが変更されないように保護する場合は、リストのゲッターメソッドを提供しないでください。
セッターがなかったので、オブジェクトはまだ元のままです。しかし、あなたがすることはそれに新しい/異なるオブジェクトを削除/追加することであり、これは問題ありません。
リスト参照は不変ですが、リストではありません。リスト自体を不変にしたい場合は、 ImmutableList の使用を検討してください