Java 这个类是不可变的吗?

Java 这个类是不可变的吗?,java,Java,下面的类没有final关键字,但其成员变量是private和final的,并且该类不公开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 S

下面的类没有final关键字,但其成员变量是private和final的,并且该类不公开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;
    }
}

如前所述,是的,类是不可变的

类声明中的“final”关键字阻止它被扩展-它与不变性无关(除非您的变量被声明为公共的或受保护的)


编辑;“不相关”是一个糟糕的词语选择,请参见下面的类本身是不可变的,是的-如果您只创建了
Abc
的实例,那么在创建实例后,该类的任何方面都不能更改

然而,这并不意味着任何接收类型为Abc的参数的代码都可以假定它是不可变的,因为它带来了所有的好处。。。因为类不是
final
。与
Abc
兼容的类型的对象完全可能是可变的:

公共类可变扩展Abc{
私有字符串值;
公共可变(int-id,字符串名){
super(id,name);
}
公共void设置值(字符串值){
这个值=值;
}
@重写公共字符串toString(){
返回值;
}
}
现在假设你有一个处理Abc的代码:

公共类ABC消费者{
私人期末考试;
公众消费者(Abc){
this.abc=abc;
}
//不需要创建防御副本或类似的东西。。。
//abc是不变的,对吗?
公共Abc getAbc(){
返回abc;
}
}
在这里,消费者认为可以将
Abc
视为一个不可变的类,但如果有人通过传递
Mutable
实例而不是“vanilla”
Abc
实例来创建
AbcConsumer
,则可能会导致问题

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


换句话说:是的,
Abc
类是不可变的。。。但是,您不能假设编译时类型为Abc的引用引用引用不可变对象。

,很可能不是

一个问题是术语。你说的课堂是什么意思?如果你是说这段代码是不可变的。但“这段代码”与不变性的概念并不特别相关。如果我们认为:“强>这个类型< /强>

,这通常会更有意义。 与中一样,类型是不可变的吗

如中所示:

public void foo(Abc abc) { ... }
假设接收到的
abc
不可能更改是否安全

然后答案是否定的。这是不安全的:类型
Abc
可变的

原因是有人可以做到这一点:

class SneakyAbc extends Abc {
    private int id;

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

    public String getId() {
       return id;
    }
}
这就是为什么几乎总是将不可变类设置为final,以充分保证它

取决于你想用“这个术语是什么意思”的画笔来画,如果<强> > <强> > ABC的方法是“代码>最终< /代码>,如果你真的想这样,你也可以认为它是不可变的:虽然类不需要是不可变的(子类可以添加新的非最终字段,并为此创建吸气剂和定位器),假设您不使用反射,您可以从Abc类型“见证”的所有内容看起来都是不变的

要进一步深入研究,需要知道不可变的确切定义

注意,像
java.io.File
这样的东西只有final字段,并且是final的,但是,它有可以修改的容易观察到的状态:just。。删除这个文件,瞧,你可以看到它。你可以用
IdentityHashMap
来做类似的特技,创造出一个人造的,但非常明显的“场”

因此,“不变”作为一个概念:有用。作为赋予特定类型或某个java源文件的布尔标志:不有用。

Records 其他答案直接回答了您关于不变性的问题,类被标记为
final
,子类是可变的。我将添加一个替代选项来更简要地实现您的不变性目标:记录

带来了新功能。如果类的主要目的是不可变且透明地传输数据,请将类定义为记录。编译器隐式地创建构造函数、getter、
equals
&
hashCode
toString

记录是隐式的
final
,因此子类不存在可变的风险

在括号中声明属性。默认情况下,您不需要在
记录的大括号主体中放入任何内容

record Abc ( int id , String name ) {}
像任何其他类一样实例化

Abc x = new Abc ( 42 , "Snuffleupagus" ) ;
隐式getter方法只是属性名。JavaBeans样式
get…
/
是…
方法命名未使用。(如果需要,您可以添加此类方法。)

鼻塞


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


在您的示例中,虽然没有类final,但由于final关键字,内部情况不能再次更改。通过这种方式,该类再次变为不可变的

“该类是否不可变?”-是的,它是不可变的。--“下面的类在类上没有final关键字“-
final
,这意味着它不能被子类化。是的,它是。虽然它不是
final
private
成员不能从子类访问;类的间接实例不一定是不可变的。”…(除非您的变量被声明为公共的或受保护的)”-或
default
(即没有修饰符)。我不会说它与不可变性“无关”。。。威尔
System.out.println( x.name() ) ;