Java 不可變物件
阿新 • • 發佈:2018-12-21
概念
官方說明如下:
The following rules define a simple strategy for creating immutable objects. Not all classes documented as "immutable" follow these rules. This does not necessarily mean the creators of these classes were sloppy — they may have good reason for believing that instances of their classes never change after construction. However, such strategies require sophisticated analysis and are not for beginners.
- Don't provide "setter" methods — methods that modify fields or objects referred to by fields.
- Make all fields
final
andprivate
.- Don't allow subclasses to override methods. The simplest way to do this is to declare the class as
final
. A more sophisticated approach is to make the constructorprivate
and construct instances in factory methods.- If the instance fields include references to mutable objects, don't allow those objects to be changed:
- Don't provide methods that modify the mutable objects.
- Don't share references to the mutable objects. Never store references to external, mutable objects passed to the constructor; if necessary, create copies, and store references to the copies. Similarly, create copies of your internal mutable objects when necessary to avoid returning the originals in your methods.
Applying this strategy to
SynchronizedRGB
results in the following steps:
- There are two setter methods in this class. The first one,
set
, arbitrarily transforms the object, and has no place in an immutable version of the class. The second one,invert
, can be adapted by having it create a new object instead of modifying the existing one.- All fields are already
private
; they are further qualified asfinal
.- The class itself is declared
final
.- Only one field refers to an object, and that object is itself immutable. Therefore, no safeguards against changing the state of "contained" mutable objects are necessary.
不可變物件的特點:
- 不要提供 setter 方法 - 修改欄位引用的欄位或物件的方法;
- 所有欄位被 final 和 private 修飾;
- 不允許子類重寫方法。最簡單的方法是將類宣告為 final。更復雜的方法是 private 在工廠方法中構造建構函式和構造例項;
- 如果例項欄位包含對可變物件的引用,則不允許更改這些物件:
- 不要提供修改可變物件的方法;
- 不要共享對可變物件的引用。永遠不要儲存對傳遞給建構函式的外部可變物件的引用; 如有必要,建立副本並存儲對副本的引用。同樣,必要時建立內部可變物件的副本,以避免在方法中返回原始物件;
- 執行緒安全性;
- 會產生大量的“垃圾”;
看這樣一個類:
package com.example.test;
import java.util.List;
/**
* @author Dongguabai
* @date 2018/12/17 14:30
*/
public final class ImmutableTest {
private final String name;
private final String address;
private final List<String> hobbies;
public ImmutableTest(String name, String address, List<String> hobbies) {
this.name = name;
this.address = address;
this.hobbies = hobbies;
}
public String getName() {
return name;
}
public String getAddress() {
return address;
}
public List<String> getHobbies() {
return hobbies;
}
}
構造這樣一個類產生的物件其實並不是不可變物件,因為物件持有的引用依然可以被修改。
String 類的不可變性
Java 中的 String 類就是一個典型的不可變類,也許會有人說 String 類不是有一個 replace() 方法嘛,那 String 還是可以更改啊,可以看看 String 的 replace() 方法:
public String replace(char oldChar, char newChar) {
if (oldChar != newChar) {
...
return new String(buf, true);
}
}
return this;
}
可以看到最終還是 new 了一個 String。
參考資料:
https://docs.oracle.com/javase/tutorial/essential/concurrency/imstrat.html