[英]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.