1. 程式人生 > >[瘋狂Java]面向物件:不可變類

[瘋狂Java]面向物件:不可變類

1. final只是保證被修飾的變數的值不可變!

    1) 注意!保證的是值不可變,什麼是值?對於一個引用來說,就是指標咯,即指標的值不變,即其指向不變,但是我還是可以通過這個指標來呼叫可以改變該物件成員的方法啊!!

    2) 即final如果修飾引用變數,只能保證該引用的指向不變,但不能保證該引用指向的物件不變,其指向的物件還是可以改變的!!

    3) 例如:final Person p = new Person("Tom");  p.setName("Peter");是允許的!!

2. 不可變類:

    1) 不可變類是指建立該類的物件後,這些物件的資料成員都不能改變;

    2) 這種需求是普遍的,例如Java的8個包裝器型別以及String類都是不可變類;

    3) 那該如何設計自己的不可變類呢?三原則:

         i. 資料成員都是private final的!這是顯然的;

         ii. 只提供getter,不能提供setter!這也是顯然的;

         iii. hashCode和equals應該根據資料成員計算得來,並且hashCode相等的情況下也要保證equals相等;

     4) 有了以上三原則就基本能保證設計的類是不可變的了;

!!什麼叫做基本?難道這樣還不能保證是不可變類嗎?

!!誠然,如果資料成員都是Integer、String這些本身就是不可變類,那當然沒有問題,但往往業務上你想包含的資料成員都是其它公司開發的類庫中的類,而很多類本身設計時都是可變的,但你往往有得不到這些類庫的原始碼,因此你無法通過改動原始碼的方式把這些類改成不可變類;

     5) 因此現在的問題是,如果你想把一個包含可變類物件的類設計成不可變類應該如何做呢?

          i. 其實思路很簡單!無非就是從兩個方面出發:

             a. 所有在類內部對該可變成員的訪問一定要保證不能對其進行修改。這是顯而易見的!根本不用說都應該能想到;

             b. 另外可能發生修改隱患的地方就是返回這個可變成員的地方了:如果一個方法返回的剛好就是這個可變成員,那麼返回後在類的外部就完全有可能對該成員進行修改了(即返回資料成員也是一種暴露類的內部情況的一種情形);

          ii. 因此需要做文章的地方就在於b.了;

          iii. 其實應對策略也很簡單,你只要建立一個該成員的副本並返回就行了,這樣的話外界拿到的僅僅是該成員的副本,對副本修改不會影響到原本;

     6) 示例:

class Test {
	private final StringBuilder name; // 故意用StringBuilder,因為它是一個可變類
	public Test(StringBuilder name) {
		this.name = name;
	}
	public StringBuilder getName() {
		return new StringBuilder(name); // 返回一個臨時建立的副本,外界對副本的改變不會影響原本
	}
}