2015-09-06 83 views
1

我讀this,雖然其中一個答案是有道理的,但我仍然覺得有什麼不妥,所以我會繼續舉個例子。不可變的類不是如此不可變

假設你有這個類:

public class ClassA { 
    private List<E> mList; 

    //Constructor assigns some value to mList... 

    public List<E> getList() { 
    return mList; 
    } 
} 

這個類被認爲是不變,然而,當我做這樣的事情在另一個類,說ClassB的,事情發生:

public class ClassB { 

    //variables and constructors.... 

    public void aFunction(ClassA classAObject) { 
    List<E> anotherList = classAObject.getList(); 

    //play around with the variable anotherList, and though ClassA is immutable, I can change the value of its mList variable. 
    } 

} 

現在我知道我可以添加'最終',就像我在上面發佈的鏈接中提到的那樣,但是認真地說,我錯過了關於這個不變概念的東西嗎?我的意思是,爲什麼它不是不可改變的,是有原因的嗎?

+3

如果你想要真正的不可變性,你將不得不返回'mList'作爲一個值,而不是一個參考。 –

+2

私有成員變量,'mList'是不可變的 - 沒有辦法重新指定它指向List的另一個實例。但是,它包含的值可以隨時隨地播放。 – jrahhali

回答

4

「final」屬性隻影響對列表的引用,而不影響列表本身。如果你想要一個不變的列表,列表對象本身必須是不可變的。值得慶幸的是,也有一些標準的解決方案是:

1

您無法更改mList的值,即爲該屬性分配不同的值。但是您可以更改mList的內容。
如果您想避免這種情況,請返回Collections.unmodifiableList(this.mList)

檢查Joshua Bloch的「Effective Java」(特別是「Minimize mutability」)。

+0

你不必每次都返回一個新的'unmodifiableList',你也可以在構造函數中設置'mList'字段。 – Clashsoft

+0

@Clashsoft那麼,我什麼都沒有說「新......每一次」。 :)但這是一個好點。 – lexicore

0

類是consi當它的所有成員字段是不可變的時候,它們都是不可變的,標量字段和引用,但不是必需的引用對象。而列表就是這樣引用的,肯定是不平凡的對象。

要讓不可變的引用對象(如引用的列表)也需要更多的努力。

如果要擴展到引用列表,首先將final關鍵字設置爲mList聲明,以使引用安全不變。好的,現在讓我們確保列表。您可以:

  • 不要暴露mList可言,也許只有ClassA.get(index)委託給mList.get()就足以滿足您的需求。您必須確保該列表填充在構造函數中,並且沒有其他修改訪問mList
  • 創建列表的防守副本,你可以放心地暴露
  • 周圍列表創建不可變的包裝適配器,使用例如:Collections.unmodifiableList(list)

但是不要忘記,該清單又僅僅是一個引用的集合,並且您再次只保護引用,它不保證引用列表中的對象,即列表項。

爲了使整個對象圖不變,如果它是你可能想要的,那將需要更多的努力。不變性在開發工作和CPU方面不一定會很便宜,就像做防禦性拷貝一樣。

1

首先,ClassA不是不可變的,也不包含List。除非在對象中提供了「最終」定義,否則引用類型的不可變性是不可能的。在給定的例子中,列表不能是不可變的,因爲它不是被設計爲。您可以使用:

public List<E> getList() { 
    return Collections.unmodifiableList(list); 
    } 

public E[] getList() { 
    return (E[]) list.toArray(); 
    } 

製作對象列表-practially,不是一成不變truly-以防止來自外部的變化。

作爲一般建議,在封裝和OOP原則方面,返回集合類型作爲參考並不是一個好的實踐。這樣你:

  1. 打開你的對象外界的內部,
  2. 使可能的意外變化(或必須思考並防止他們)
  3. 一定的時間(指後代碼從更多的地方被調用並且更多的地方被調用),這使得你的類很難改變並且採用未來的需求 。

這就是爲什麼你必須從外部世界抽象對象的內部部分,並打開只需要的部分。一般而言,參考類型的不變性更多的是它們的抽象和封裝,而不是外在提供。