[英]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
不可能改變嗎?
然后答案是否定的。 假設這是不安全的:類型Abc
是mutable 。
原因是有人可以這樣做:
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
& hashCode
和toString
。
記錄是隱含的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.