这个类是否是不可变的?

  • 本文关键字:不可变 是否是 java
  • 更新时间 :
  • 英文 :


下面的类没有final关键字,但它的成员变量是privatefinal,并且类没有暴露mutate/set方法。这个类是否是不可变的?

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;
}
}

类本身是不可变的,是的-如果您只创建Abc的实例,那么在创建实例之后,没有任何方面可以更改。

然而,这并不意味着任何接受Abc类型参数的代码都可以假设它是不可变的,并且具有…因为这门课不是final。与Abc兼容的类型的对象完全有可能是可变的:

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视为不可变类-但是如果有人通过传递Mutable实例而不是"香草"来创建AbcConsumer,则可以将其视为不可变类。在Abc实例中,它可能会导致问题。

这就是为什么当你创建一个不可变类型时,让它也成为final通常是一个好主意——这样任何消费者都知道,如果他们收到该类型的引用,它肯定是不可变的。

换句话说:是的,Abc类是不可变的…但是你不能假设编译时类型为Abc的引用指向一个不可变对象。

从目前的情况来看,这个类是不可变的。

"final"关键字可以防止类被扩展——它与不可变性无关(除非你的变量被声明为public或protected)。

编辑;"不是related"是一个糟糕的选择词,请看下面乔恩·斯基特的回答

一个问题是术语。你说的是什么意思?如果你指的是这个代码当然,它是不可变的。但是"这段代码"并不是与不变性概念特别相关的东西。如果我们考虑一下,这通常更有意义:this type.

同样,的类型吗?Abc不变的吗?

As in, given:

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

假设收到的abc不可能改变是安全的吗?

然后答案是no。这样假设是不安全的:类型Abc可变的.

原因是有人可以这样做:

class SneakyAbc extends Abc {
private int id;
public void setId(int id) {
this.id = id;
}
public String getId() {
return id;
}
}

这就是为什么不可变类实际上总是被设置为final,以完全保证它。

取决于你想用"这个术语是什么意思"的画笔画得多花哨,如果所有Abc的方法是final,如果你真的想的话,你可以认为它是不可变的:虽然类不需要是不可变的(子类可以添加一个新的非final字段,并为此创建getter和setter),所有的东西你可以从Abc类型"见证",假设你不使用反射,看起来是不可变的。

你使用的不可变的确切定义将需要知识来进一步研究。

请注意,像java.io.File这样的东西只有final字段,并且是final的,然而,它有很容易观察到的状态,可以修改:只是…删除文件,你就可以看到它了。你可以用IdentityHashMap来创建一个人造的,但仍然非常明显的"场"。

因此,"不可变"作为一个概念:有用。作为布尔标志赋予某种类型或某些java源文件:没用。

Records

其他答案直接解决了您关于不可变性,类被标记为final和子类是可变的问题。我将添加另一个选项以更简单地实现不变性的目标:Records

Java 16带来了新的记录特性。如果类的主要目的是不可变且透明地携带数据,则将类定义为记录。编译器隐式地创建默认构造函数、getter、equals&hashCodetoString.

一个记录是隐式的final,所以没有子类变的风险。

在括号中声明属性。默认情况下,您不需要在record的花括号正文中放入任何内容。

record Abc ( int id , String name ) {}

像其他类一样实例化。

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

隐式getter方法只是属性名。javabeans风格的get…/is…方法命名是而不是。(如果需要,可以添加这样的方法。)

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

Snuffleupagus

如果它的内部状态可以在类创建后改变,则它是可变的

在你的例子中,虽然没有final类,但是由于final关键字,内部情况不能再改变。这样,类又变成不可变的

最新更新