簡體   English   中英

這 class 是否不可變?

[英]Is this class immutable or not?

下面的 class 沒有 final 關鍵字,但它的成員變量是 private 和 final 的,並且 class 沒有公開 mutate/set 方法。 這 class 是否不可變?

public class Abc {

    private final int id;
    private final String name;

    public Abc(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public int getId() {
        return id;
    }
}

class 本身是不可變的,是的 - 如果您僅創建Abc的實例,則在創建實例后無法更改任何方面。

但是,這並不意味着任何接收Abc類型參數的代碼都可以假定它是不可變的,並具有帶來的所有好處……因為 class 不是final Abc兼容的類型的 object 完全有可能是可變的:

public class Mutable extends Abc {
    private String value;

    public Mutable(int id, String name) {
        super(id, name);
    }

    public void setValue(String value) {
        this.value = value;
    }

    @Override public String toString() {
        return value;
    }
}

現在想象你有處理Abc的代碼:

public class AbcConsumer {

    private final Abc abc;

    public AbcConsumer(Abc abc) {
        this.abc = abc;
    }

    // No need to create a defensive copy or anything like that...
    // abc is immutable, right?
    public Abc getAbc() {
        return abc;
    }
}

在這里,消費者假設Abc視為不可變的 class 是可以的——但如果有人通過傳入Mutable實例而不是“普通” Abc實例來創建AbcConsumer ,則可能會導致問題。

這就是為什么在創建不可變類型以使其成為final類型時通常是一個好主意的原因——這樣任何消費者都知道,如果他們收到該類型的引用,它肯定是不可變的。

換句話說:是的, Abc class 是不可變的……但是您不能假設具有Abc編譯時類型的引用指的是不可變的 object。

如前所述,是的,class 是不可變的。

class 聲明上的“final”關鍵字阻止它被擴展 - 它與不變性無關(除非您的變量被聲明為公共或受保護的)。

編輯; “不相關”是一個糟糕的詞選擇,請參閱下面Jon Skeet 的回答

,很可能不是。

一個問題是術語。 你說的class是什么意思? 如果你的意思是這段代碼,當然,它是不可變的。 但是“這段代碼”並不是與不變性概念特別相關的東西。 如果我們考慮一下,這通常會更有意義:這種類型

例如, Abc類型是不可變的嗎?

如,給定:

public void foo(Abc abc) { ... }

可以安全地假設收到的abc不可能改變嗎?

然后答案是否定的。 假設這是不安全的:類型Abcmutable

原因是有人可以這樣做:

class SneakyAbc extends Abc {
    private int id;

    public void setId(int id) {
       this.id = id;
    }

    public String getId() {
       return id;
    }
}

這就是為什么不可變類幾乎總是被設為final以完全保證它的原因。

取決於你想用“這個術語是什么意思”的畫筆來畫的花哨,如果 Abc 的所有方法都是final ,如果你真的想的話,你也可以認為它是不可變的:雖然 class 不需要是不可變的(子類可以添加一個新的非最終字段並為此創建 getter 和 setter),假設您不使用反射,您可以從 Abc 類型“見證”的所有內容確實看起來是不可變的。

go 對不可變的確切定義需要進一步研究的知識。

請注意,像java.io.File類的東西只有最終字段並且是最終的,但是,它很容易觀察到 state 可以修改。 您可以使用IdentityHashMap拉出類似的特技來創建一個虛假但非常可觀察的“場”。

因此,“不可變”作為一個概念:有用。 作為 boolean 標志以賦予某種類型或某些 java 源文件:無用。

記錄

其他答案直接解決了您關於不變性的問題, class 被標記為final ,子類是可變的。 我將添加一個替代選項來更簡單地實現您的不變性目標: Records

Java 16帶來了新的記錄功能。 如果您的 class 的主要目的是不可變和透明地攜帶數據,請將您的 class 定義為記錄。 編譯器隱式創建構造函數、getter、 equals & hashCodetoString

記錄是隱含的final ,因此沒有子類變得可變的風險。

在括號中聲明屬性。 默認情況下,您不需要在record的花括號主體中添加任何內容。

record Abc ( int id , String name ) {}

像任何其他 class 一樣實例化。

Abc x = new Abc ( 42 , "Snuffleupagus" ) ;

隱式 getter 方法只是屬性名稱。 使用 JavaBeans 樣式的get… / is…方法命名。 (如果需要,您可以添加此類方法。)

System.out.println( x.name() ) ;

Snuffleupagus

如果在創建 class 之后可以更改其內部狀態,則它是可變的。

在您的示例中,雖然沒有 class final,但由於 final 關鍵字,內部情況無法再次更改。 這樣,class 再次變為不可變

暫無
暫無

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

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