2015-05-14 54 views
2

除了下面的內容外,還有其他什麼方法可以使下面的類不可變。如何製作下面的java類不可變

  • 沒有二傳手
  • 所有字段是私人和最終
  • 類被聲明爲final這樣的方法不能在子類中重寫

    public final class ImmutableClass { 
    
        private final String name; 
    
        private final List<Integer> listOfNumbers; 
    
        public ImmutableClass(String name, List<Integer> listOfNumbers) { 
         this.name = name; 
         this.listOfNumbers = listOfNumbers; 
        } 
    
        public String getName() { 
         return name; 
        } 
    
        public List<Integer> getListOfNumbers() { 
         return listOfNumbers; 
        } 
    } 
    
+2

'this.listOfNumbers = Collections.unmodifiableList(new ArrayList <>(listOfNumbers));' –

+1

您的列表是可變的。返回一個不可變的版本或一個副本。 – njzk2

回答

6

是的。你需要在構造函數中提供一個列表的防禦副本,並在getter中創建另一個副本。或者,使用Google Guava的ImmutableList類,您可以將複製保存在getter中。

public ImmutableClass(String name, List<Integer> listOfNumbers) { 
    this.name = name; 
    // you need to null check your parameter here first 
    this.listOfNumbers = ImmutableList.copyOf(listOfNumbers); 
} 

這確保了getter返回的對象不能與客戶端篡改,即使它是你在現場保存同一個對象實例。

如果你想成爲真正的迂腐,你仍然可以寫你的getter這樣相對小的開銷:

public List<Integer> getListOfNumbers() { 
    return ImmutableList.copyOf(listOfNumbers); 
} 

由於ImmutableList.copyOf() will try to avoid making a copy when it's safe to do so,這也不會真正創造一個新的副本,所以沒有太多點將其放入。

Ps:在您可能需要執行的任何前提條件下,在構造函數中檢查輸入參數也是一種很好的做法。 (例如,列表不能爲空,並且不能爲空)。這些檢查應該在副本上執行總是,除了空檢查之外,在創建副本之前需要執行該檢查。但是這一點並不是永恆不變的,而是編寫保持其不變性的安全代碼,然而客戶試圖破壞它們。

2

listOfNumbers需要在複製構造函數和列表的getter需要返回列表的副本。就目前而言,你正在返回一個可變列表,這違反了類的不變性。

或者,您可以使用不可變列表實現,例如,從Guava

+1

在構造函數中也做一個副本也很重要。 – biziclop

6

您應該防禦性地複製傳入構造函數的listOfNumbers,並在getter中返回它的不可變視圖。

2

看一看約書亞布洛赫的書Effective Java,特別是第15項

他提供了有關如何使類不可變的一個驚人的解釋。

1

正如其他人已經正確回答,您需要確保沒有人可以修改listOfNumbers字段。

但是,您可以使用我編寫的名爲Mutability Detector的自動工具獲得相同的答案,並且當您想測試其他想要使其不變的類時,它可能會派上用場。

鑑於你ExampleClass,和下面的單元測試:

import org.junit.Test; 
import static org.mutabilitydetector.unittesting.MutabilityAssert.assertImmutable; 

public class Question_30240358 { 

    @Test 
    public void isImmutable() { 
     assertImmutable(ImmutableClass.class); 
    } 
} 

結果是失敗的單元測試,並顯示以下消息:

Expected: org.mutabilitydetector.stackoverflow.ImmutableClass to be IMMUTABLE 
    but: org.mutabilitydetector.stackoverflow.ImmutableClass is actually NOT_IMMUTABLE 
    Reasons: 
     Attempts to wrap mutable collection type using a non-whitelisted unmodifiable wrapper method. [Field: listOfNumbers, Class: org.mutabilitydetector.stackoverflow.ImmutableClass] 
    Allowed reasons: 
     None. 
     at org.mutabilitydetector.unittesting.internal.AssertionReporter.assertThat(AssertionReporter.java:48) 
     at org.mutabilitydetector.unittesting.MutabilityAsserter.assertImmutable(MutabilityAsserter.java:108) 
     at org.mutabilitydetector.unittesting.MutabilityAssert.assertImmutable(MutabilityAssert.java:672) 
     at org.mutabilitydetector.stackoverflow.Question_30240358.isImmutable(Question_30240358.java:14) 

此測試將通過,如果字段賦值更改爲:

this.listOfNumbers = Collections.unmodifiableList(new ArrayList<Integer>(listOfNumbers)); 

該測試將捕獲許多其他類型的問題,引入可變性。

+0

謝謝。這將是非常有用的。 – Jay