繁体   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