2010-06-29 58 views

回答

21

不,它不是因爲數組的元素仍然可以改變。

int[] v1 = new int[10]; 
MyClass v2 = new MyClass(v1); 
v1[0] = 42; // mutation visible to MyClass1 
+0

如何?通過反射? – 2010-06-29 16:47:01

+0

@Vivin添加了一個代碼示例 – JaredPar 2010-06-29 16:47:21

+0

啊對!我一直忘記將參考文獻分配給私人會員。 – 2010-06-29 16:48:50

0

沒有辦法讓數組不可變。這是沒有辦法讓任何客戶端代碼設置或刪除或添加項目數組。

這裏是一個真正的替代不變:(!這是我從讀書有效的Java保留 - 一個偉大的書)

private static class MyClass 
{ 
    private List<Integer> list; 

    private MyClass(final int[] array) 
    { 
     final List<Integer> tmplist = new ArrayList<Integer>(array.length); 
     for (int i : array) 
     { 
      tmplist.add(array[i]); 
     } 
     this.list = Collections.unmodifiableList(tmplist); 
    } 
} 
+0

不,不可修改列表和asList創建視圖。因此,像JaredPar的答案一樣對數組進行外部更改將導致此版本的MyClass發生更改。 – ILMTitan 2010-06-29 18:42:44

+0

'Arrays.asList(array)'也會返回一個'List '。 – 2010-06-29 23:24:44

+0

誰曾經低調不理解不可變的合同。 如果你返回一個可以改變的數據結構,不管它是否是某個支持數組的COPY,它仍然是__NOT__不可變的。克隆一些東西並返回一個可變的克隆__NOT__使一個類100%不可變,這也違反了最不驚人的原則。 asList不會__NOT__創建一個「視圖」,它將創建一個數組的__MUTABLE__副本作爲List。不可修改就是這樣,它返回一個不可修改的列表,因此不可變。 – 2010-06-30 14:33:34

6

我的兩個關於永恆規則美分:

  1. 不提供方法可以修改對象的狀態。
  2. 使所有的領域最終。
  3. 確保您的班級不可擴展。
  4. 使你所有的領域都是私人的。
  5. 提供獨家訪問您的班級的任何字段或組件可以被更改。本質上這適用於你的情況(如explained by JaredPar)。一個使用你的類的人仍然有一個對你的數組的引用。與此相反的是,您將的參考文獻返回到您班級的某個組件。在這種情況下,請始終創建防禦副本。在你的情況下,你不應該分配參考。相反,將您的類的用戶提供的數組複製到您的內部組件中。
+0

並且不要實現'Serializable'。 – 2010-06-29 23:23:17

1

「不變性」是程序員和他自己之間的慣例。編譯器可能會或多或少地執行該約定。

如果一個類的實例在應用程序代碼執行的正常過程中沒有更改,則它們是「不可變的」。在某些情況下,我們知道他們不會更改,因爲代碼實際上禁止它;在其他情況下,這只是我們如何使用課程的一部分。例如,一個java.util.Date實例是正式可變的(其上有一個setTime()方法),但習慣上將它作爲不可變的處理;這只是應用範圍內的約定,不應調用Date.setTime()方法。

至於其他注意事項:

  • 不變性往往是在「外部特徵」的術語想到的。例如,Java的String被記錄爲不可變的(這就是Javadoc所說的)。但是,如果您查看源代碼,則會看到String實例包含一個名爲hash的私有字段,該字段可能會隨時間而改變:這是針對由hashCode()返回的值的緩存。我們仍然認爲String是不可變的,因爲hash字段是內部優化,從外部看不到任何效果。
  • 經過反思,如果程序員希望如此地夠用,可以修改最私密的實例字段(包括標記爲final的字段)。並不是說這是一個好主意:它可能會破壞使用所述實例的其他代碼段所使用的假設。正如我所說的,不變性是一個慣例:如果程序員想要自己戰鬥,那麼他可以,但是這會對生產力產生不利的副作用...
  • 大多數Java值實際上是引用。由你來定義一個被引用的對象是否是你認爲是「實例內容」的一部分。在你的類中,你有一個引用一個(外部提供的)整數數組的字段。如果之後該陣列的內容被修改,您是否會認爲這會破壞MyClass實例的不變性?這個問題沒有通用的答案。
0

爲了使一個類不可變,你需要確保它上面的所有字段都是final的,並且這些字段的類型也是不可變的。

這可能是一個需要記住的痛苦,但有一個工具可以幫助你。

Pure4J提供了一個註釋@ImmutableValue,您可以將它添加到接口或類中。

有一個maven插件可以在編譯時檢查您是否符合此後的不變性規則。

希望這會有所幫助。