不可變對象不應該是可擴展的。爲什麼?
因爲擴展它將允許直接訪問字段(如果它們是protected
,它允許寫入方法改變它們),或添加可能是可變的狀態。
想象一下,我們寫了一個類FlexiblyRoundableDouble
,延伸Double
,它有一個額外的字段roundingMode
,讓我們選擇一個「舍入模式」。你可以爲這個字段編寫一個setter,現在你的對象是可變的。
您可以爭辯說,如果所有方法都設置爲final,則無法更改對象的原始行爲。如果您將對象分配給Double
變量,唯一可以訪問roundingMode
字段的方法是不具有多態可用性的新方法。但是,當一個班級的合同說它是不可改變的,你可以根據這個做出決定。例如,如果您爲具有Double
字段的類編寫clone()
方法或複製構造函數,則知道您不需要深度複製Double
字段,因爲它們不會更改其狀態,因此可以安全地共享兩個克隆之間。另外,你可以編寫返回內部對象的方法,而不用擔心調用者會改變那個對象。如果這個對象是可變的,那麼你就不得不做一個「防禦性拷貝」。但是,如果它是不可變的,那麼返回對實際內部對象的引用是安全的。
但是,如果有人分配FlexiblyRoundableDouble
您Double
領域之一,會發生什麼?該對象將是可變的。 clone()
會認爲它不是,它將在兩個對象之間共享,甚至可能由方法返回。然後調用者可以將其轉換爲FlexiblyRoundableDouble
,更改字段...並且它會影響使用同一實例的其他對象。
因此,不可變對象應該是最終的。
所有這些與構造函數問題無關。對象可以使用公共構造函數安全不變(如String
,Double
,Integer
和其他標準Java不可變)。靜態工廠方法只是一種方式,它利用了對象是不可變的事實,並且其他幾個對象可以安全地保存對它的引用,以創建具有相同值的較少對象。
你當然不能允許不可變的類被擴展。這是一個類不可變的關鍵要求之一。 – 2015-04-06 11:55:26
您可以看到在基元類型的包裝類中使構造函數保持私有的方法示例:「整數」,「雙精度」等。當不可變時,對象通常被視爲值。因此,相同的值是僅由一個對象還是可能由多個對象表示並不重要。因此,提供了一個靜態工廠方法,可以爲相同的值返回相同的對象。 – Seelenvirtuose 2015-04-06 11:58:49
最後一堂課,例如'java.lang.String'是**'final' **,實例化後既不能擴展也不能修改。 – 2015-04-06 11:59:28