2015-04-06 86 views
3

我正在閱讀關於在Effective Java中創建不可變類時需要遵循的特定準則。創建不可變類的最佳設計方法

我讀過In Immutable類的方法不應該被允許重寫,否則重寫的方法可能會改變方法的行爲。以下是該設計方法在Java中可用來解決這個問題: -

  1. 我們可以標記級決賽,但按照我的理解,它有一個缺點,它使類不可延伸。

  2. 其次是使個別方法最終,但我不能得到其他的缺點,除了我們需要單獨將每個方法標記爲final以防止重寫。

  3. 根據本書,更好的方法是使構造函數爲private或package-private,並提供用於創建對象的公共靜態工廠方法。

我的問題是:即使我們在類中包含私有或默認的構造函數,它不能再在同一個包(在包私有構造的情況下,其它封裝)延伸,它有一個相同的問題,其第一個有。它如何被認爲是比以前更好的方法?

+4

你當然不能允許不可變的類被擴展。這是一個類不可變的關鍵要求之一。 – 2015-04-06 11:55:26

+1

您可以看到在基元類型的包裝類中使構造函數保持私有的方法示例:「整數」,「雙精度」等。當不可變時,對象通常被視爲值。因此,相同的值是僅由一個對象還是可能由多個對象表示並不重要。因此,提供了一個靜態工廠方法,可以爲相同的值返回相同的對象。 – Seelenvirtuose 2015-04-06 11:58:49

+0

最後一堂課,例如'java.lang.String'是**'final' **,實例化後既不能擴展也不能修改。 – 2015-04-06 11:59:28

回答

2

提供靜態工廠方法爲您提供實現享元模式的空間。

他們說你應該隱藏使用構造函數創建一個新對象的可能性,而應該調用一個方法來檢查具有類似狀態的對象是否存在於「對象池」中充滿了等待重新使用的物體)。不重新使用不可變對象會浪費內存;這就是爲什麼String文字被鼓勵,並且new String()被避免(除非需要)。

class ImmutableType { 
    private static final Map<Definition, ImmutableType> POOL = new HashMap<>(); 

    private final Definition definition; 

    private ImmutableType(Definition def) { 
     definition = def; 
    } 

    public static ImmutableType get(Definition def) { 
     if(POOL.contains(def)) 
       return POOL.get(def); 
     else { 
       ImmutableType obj = new ImmutableType(def); 
       POOL.put(def, obj); 

       return obj; 
     } 
    } 
} 

Definition存儲ImmutableType的狀態。如果池中已經存在具有相同定義的類型,請重新使用它。否則,創建它,將其添加到池中,然後將其作爲值返回。

至於關於標記類final的聲明,不可變類型首先不應該是可擴展的(以避免可能的修改行爲)。標記每個方法final對於不可變類都是瘋狂的。

2

不可變對象不應該是可擴展的。爲什麼?

因爲擴展它將允許直接訪問字段(如果它們是protected,它允許寫入方法改變它們),或添加可能是可變的狀態。

想象一下,我們寫了一個類FlexiblyRoundableDouble,延伸Double,它有一個額外的字段roundingMode,讓我們選擇一個「舍入模式」。你可以爲這個字段編寫一個setter,現在你的對象是可變的。

您可以爭辯說,如果所有方法都設置爲final,則無法更改對象的原始行爲。如果您將對象分配給Double變量,唯一可以訪問roundingMode字段的方法是不具有多態可用性的新方法。但是,當一個班級的合同說它是不可改變的,你可以根據這個做出決定。例如,如果您爲具有Double字段的類編寫clone()方法或複製構造函數,則知道您不需要深度複製Double字段,因爲它們不會更改其狀態,因此可以安全地共享兩個克隆之間。另外,你可以編寫返回內部對象的方法,而不用擔心調用者會改變那個對象。如果這個對象是可變的,那麼你就不得不做一個「防禦性拷貝」。但是,如果它是不可變的,那麼返回對實際內部對象的引用是安全的。

但是,如果有人分配FlexiblyRoundableDoubleDouble領域之一,會發生什麼?該對象將是可變的。 clone()會認爲它不是,它將在兩個對象之間共享,甚至可能由方法返回。然後調用者可以將其轉換爲FlexiblyRoundableDouble,更改字段...並且它會影響使用同一實例的其他對象。

因此,不可變對象應該是最終的。


所有這些與構造函數問題無關。對象可以使用公共構造函數安全不變(如String,Double,Integer和其他標準Java不可變)。靜態工廠方法只是一種方式,它利用了對象是不可變的事實,並且其他幾個對象可以安全地保存對它的引用,以創建具有相同值的較少對象。