如何讓Java類不可變
阿新 • • 發佈:2019-01-07
不可變類:一旦建立,狀態無法改變
關於建立不可變類有很多規則,下面一一介紹這些規則:
目錄
- 定義不可變類的益處
- 定義不可變類指南
定義不可變的益處
- 構造簡單,便於測試和使用
- 不可變類自然是執行緒安全的,無需關心多執行緒和同步問題
- 不需要實現clone
- 可以延遲載入,快取它的返回值
- 由於不可變可以用於Map的key和Set的元素(set元素不能重複)
- 當作為屬性時,不需要深度clone
如何讓類不可變
在Java文件中,有關於如何定義不可變類指南: click here
- 不提供setter方法,setter方法用於修改屬性和物件引用
這個原則闡述了在你類定義的所有可變屬性中,不提供setter方法,setter方法意味著你能夠改變這個屬性的狀態。必須阻止提供setter方法 - 所有的屬性修飾新增private和final
這是另外一種增加不可變的方式,屬性宣告為private為了在類之外不能夠被訪問到,final修飾符為了讓你不能隨便的改變它們 - 不允許子類重寫方法
最簡單的方式宣告類為final,final類不允許被重寫 - 當屬性中存在可變物件變數時,要特別留意
永遠銘記你的物件變數,不是可變的就是不可變的(這句好像是廢話。。),識別出來可變物件,對可變物件的內容進行copy,並建立一個新物件賦值給它,這樣保證可變物件的不可變,通過直接copy物件內容的形式,保持資料不可變 來點優雅的,定義一個private的構造方法,通過 工廠方法構造物件
只說太抽象,還是來點例項痛快
import java.util.Date;
/**
* Always remember that your instance variables will be either mutable or immutable.
* Identify them and return new objects with copied content for all mutable objects.
* Immutable variables can be returned safely without extra effort.
* */
public final class ImmutableClass
{
/**
* Integer class is immutable as it does not provide any setter to change its content
* */
private final Integer immutableField1;
/**
* String class is immutable as it also does not provide setter to change its content
* */
private final String immutableField2;
/**
* Date class is mutable as it provide setters to change various date/time parts
* */
private final Date mutableField;
//Default private constructor will ensure no unplanned construction of class
private ImmutableClass(Integer fld1, String fld2, Date date)
{
this.immutableField1 = fld1;
this.immutableField2 = fld2;
this.mutableField = new Date(date.getTime());
}
//Factory method to store object creation logic in single place
public static ImmutableClass createNewInstance(Integer fld1, String fld2, Date date)
{
return new ImmutableClass(fld1, fld2, date);
}
//Provide no setter methods
/**
* Integer class is immutable so we can return the instance variable as it is
* */
public Integer getImmutableField1() {
return immutableField1;
}
/**
* String class is also immutable so we can return the instance variable as it is
* */
public String getImmutableField2() {
return immutableField2;
}
/**
* Date class is mutable so we need a little care here.
* We should not return the reference of original instance variable.
* Instead a new Date object, with content copied to it, should be returned.
* */
public Date getMutableField() {
return new Date(mutableField.getTime());
}
@Override
public String toString() {
return immutableField1 +" - "+ immutableField2 +" - "+ mutableField;
}
}
驗證以上不可變類:
import java.util.Date;
public class MainTest
{
public static void main(String[] args)
{
ImmutableClass im = ImmutableClass.createNewInstance(100,"test", new Date());
System.out.println(im);
tryModification(im.getImmutableField1(),im.getImmutableField2(),im.getMutableField());
System.out.println(im);
}
private static void tryModification(Integer immutableField1, String immutableField2, Date mutableField)
{
immutableField1 = 10000;
immutableField2 = "test changed";
mutableField.setDate(10);
}
}
輸出結果如下:
100 - test - Tue Jun 09 23:14:01 CST 2015
100 - test - Tue Jun 09 23:14:01 CST 2015
從輸出中可以看出:即使通過物件引用改變物件變數,值依然不改變,因此類是不可變類