簡體   English   中英

如果我們記錄它的不變性,我們可以將對象視為不可變的

[英]Can we treat an object as immutable if we document its immutability

例如,我有這個:

public class Container{

    private final List<String> strs;

    /**
    * Contructs {@code Container} by a given {@code List}
    * The content of the list referenced should not be modified 
    * after the constructor invocation.
    */
    public Container(List<String> strs){
        this.strs = strs;
    }
    //Other staff
}

Container的狀態可以在構建后修改,但我們禁止在文檔中。 該對象可以被視為不可變的嗎? 在構造函數中進行復制並不是完全可取的。

不變性是針對線程安全的。

如果您不想復制列表,並且仍然希望它實際上是不可變的,那么您可以使用持久數據結構,例如Clojure中的 數據結構

在Clojure中,所有值都是不可變的,因此您始終可以安全地傳遞它們。 當有人將一個項目添加到列表的前面時,這會邏輯上創建一個新列表,但實際上它只添加一個指向舊列表頭部的元素,而不必復制所有以前的列表。 Clojure中的所有數據結構都是這樣的,您也可以在Java中使用它們(另請參閱什么是在java中使用的好的持久性集合框架? )。

或者,您可以使用pcollections

通常,您不應將類的內部結構暴露給外部。 這對於imutables來說非常重要。

因此,我建議在構造函數中使用new ArrayList(x)創建一個副本。

此外,您可以使用Collections.unmodifiableList()來防止從類內部進行修改。

我建議將兩者結合起來,看起來像這樣:

import java.util.Collections;
...

public Container(List<String> strs){
    this.strs = Collections.unmodifiableList(new ArrayList<>(strs));
}

您的容器將在構造函數調用時記住列表的成員。 如果列表在其他地方更改,它將不會對您的imutable產生任何影響。

即使容器中的代碼也無法修改列表 - 該列表會拋出UnsupportedOperationException

完整的,有效的示例代碼:

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class X {

    public static void main(String[] args) {

        // create a list
        List<String> myList = new ArrayList<>();
        myList.add("a");
        myList.add("b");

        // hand it over to the container
        Container container = new Container(myList);

        // modify it afterwards
        myList.add("BUH!");

        // check contents of container
        for (String item : container.strs) {
            System.out.println(item);
        }
    }

}

class Container{

    final List<String> strs;

    /**
    * Contructs {@code Container} by a given {@code List}
    * The content of the list referenced should not be modified 
    * after the constructor invokation.
    */
    public Container(List<String> strs){
        this.strs = Collections.unmodifiableList(new ArrayList<>(strs));
    }
    //Other staff
}

它輸出:

a
b

(不輸出BUH!

您可以只記錄它以提高性能,但我只建議使用非公共API的方法。 在我的公司中,我們構建了一個框架,並設法取消一些這些保護以提高性能/減少內存使用,但僅限於內部代碼。 我們決定假設框架開發人員不會亂七八糟,而且效果很好。

如果您的API將暴露給外部的其他開發人員,則應始終保持安全,特別是出於安全原因。 如果您確實需要提高性能/內存使用率,那么這是一個選項,但請謹慎應用。

您允許這種結構的決定是保證正確性和性能之間的權衡。

構建器模式可能會為您提供第三種處理方式(取決於它是否適用於您的列表構造):

要構造Container創建一個Builder ,它收集列表元素,然后最終調用(私有)Container構造函數。 這樣可以方便地收集列表元素,並避免在構造最終對象時復制列表:

public class Container {
     public static class Builder {
         public Builder add(String s) { strs.add(s); return this; }
         public Container create() { 
               Container c = new Container(Collections.unmodifiableList(strs)); 
               strs = null; 
               return c; 
         }
         private List<String> strs = new ArrayList<>();
     }

     private final List<String> strs;

     private Container(List<String> strs){
          this.strs = strs;
     }
}

一般來說,沒有。 可以在線程之間安全地共享不可變對象。 在線程之間共享的安全對象不僅僅是文檔問題; 它的類必須以特定的方式實現。 說“這個類的對象是不可變的”沒有一個讓它變得不可變的實現會主動誤導,一個不真實,應該被視為一個bug。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM