Effective Java Item 15 note

Minimize mutability

Arwii
mycodingjourney
5 min readJun 11, 2018

--

Immutable class — instance不能被修改的class,資訊要在constructor的時候就提供,在lifetime內被創立出來後就固定不變。

Ex: String, Primitive wrapper, Big Integer, BigDecimal。

那有什麼好處呢?

  1. 比較簡單,只有一種狀態。
  2. 本質上是thread safe的,所以object可以被共享,就像item 1提到的,可以節省memory的使用跟gc的成本。
  3. 不用維護clone或者是copy constructor(item 11)。
  4. 此外應該會比較適合現在的functional programming

缺點是:

如果每個值都要產生一個object的情況下,成本就會比較高。書上提供了BigInteger跟BitSet的例子。另外提的是,如果因為這個缺點,但class還是要使用到複雜的操作,那麼看看自己可不可以預測到client會怎麼使用複雜的操作然後把這個操作wrapper起來自己優化它,就像是BigInteger裡面的modular exponentiation,如果不能預測也沒關係,可以有個mutable的配套class,像是String之於StringBuilder。

要成為immutable class有五個條件:

  1. 不要提供可以修改object state的method,像是setter之類的,也不能讓field成為public被修改到。
  2. 保證class不會被extends,避免subclass可以修改到parent是immutable的狀態,一般會把class設成是final以避免extends。
    另一種就是把constructor全部都是private or packaged-private,然後開放static factory去取代public constructor(item 1),沒有public constructor是沒有辦法extends的,而好處是可以把物件產生隱藏起來,對使用上會比較靈活一點,如果以後要對物件修改還是添加東西也不用改到client端,另一個好處就是如果要cache的話也方便。
  3. 所有的field都是final。
  4. 所有的field都是private。(書上最後有提到,如果是因為效能的話,例如產生物件的消耗昂貴,那就該field不要是externally visible就好,也就是說如果有個不是final的變數,需要被lazy initialization的話也是可以接受的,只要不要被外面看到就好)
  5. 如果class中有field reference到mutable的class,那麼要確保client不能取得這些reference,以避免對其值做修改。也不要用client提供的reference做初始化。可以採用defensive copy的方法保護,返回一個新的物件。(item 39),書上有提到大多數重要的immutable class都使用這種方式,叫做functional,稍微看一下別人的心得後,這邊提到的functional應該是一個FP的概念之一沒錯。

要注意到BigInteger跟BigDecimal因為歷史因素,所以使用上要小心用到的是不是自己預期的immutable class。

那麼,除非必要,不要替每個field都寫個對應的setter,盡量的immutable,可降低物件數量,也可以更方便的debug,所以field盡可能的話都要是final的。constructor or static factory就應該做完初始化,除非有別的一定要的理由,不然不應該提供其他public的初始化method或者是重新初始化,這樣會讓該object可以被重用。

這邊岔題一下看到functional就想到是不是那個functional,所以就找了一下Java上的functional programming,先稍微了解一下,等到Effective Java看完之後會找時間再深入研究(https://ihower.tw/blog/archives/6305 可以參考看看),底下完全是自己筆記,請不要參考,都還有待細看。
=================筆記==================

FP的目的:提高concurrency,底下是考量點。

a. Concurrent在OO一定會遇到,要自己處理synchronized,所以FP提供Concurrency的抽象機制(!!!可以找找看)

b. 資料量大的時候

c. 限制模組化的彈性,deployment的便利,程式碼的簡化。

FP是什麼:

a. 避免Mutable的狀態。

b. function的傳遞。(lambda(Java 8後有支援,可以想成inner anonymous class,就是個anonymous function),closure(可以在function內使用的外面的變數))

c. 不管什麼context,只要參數一樣結果都一樣。

d. 要避免counter是個mutable的考慮,所以用recursive取代for loop,但recursive就會造成stack過多的效能問題,FP適用tail-call recursive(見https://zh.wikipedia.org/wiki/%E5%B0%BE%E8%B0%83%E7%94%A8)去解,但Java目前不支援(https://stackoverflow.com/questions/22866491/does-java-8-have-tail-call-optimization 有人提到可以參考看看本書)。

e. Lazy Evaluation,Declarative(不是一行行的步驟,感覺看起來像是很多的recursive)

=================筆記結束==================

--

--

Arwii
mycodingjourney

Try not to become a man of success, but rather try to become a man of value — A. Einstein