简体   繁体   English

这 class 是否不可变?

[英]Is this class immutable or not?

The below class doesn't have final keyword but its member variables are private and final and the class exposes no mutate/set methods.下面的 class 没有 final 关键字,但它的成员变量是 private 和 final 的,并且 class 没有公开 mutate/set 方法。 Is this class immutable or not?这 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;
    }
}

The class itself is immutable, yes - if you create an instance of just Abc , no aspect of that can be changed after the instance has been created. class 本身是不可变的,是的 - 如果您仅创建Abc的实例,则在创建实例后无法更改任何方面。

However, that doesn't mean that any code receiving a parameter of type Abc can assume it's immutable with all the benefits that carries... because the class isn't final .但是,这并不意味着任何接收Abc类型参数的代码都可以假定它是不可变的,并具有带来的所有好处……因为 class 不是final It's entirely possible for an object of a type compatible with Abc to be mutable: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;
    }
}

Now imagine you've got code which deals with an Abc :现在想象你有处理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;
    }
}

Here the consumer assumes it's fine to treat Abc as if it's an immutable class - but if someone creates an AbcConsumer by passing in a Mutable instance instead of "vanilla" Abc instance, it could cause problems.在这里,消费者假设Abc视为不可变的 class 是可以的——但如果有人通过传入Mutable实例而不是“普通” Abc实例来创建AbcConsumer ,则可能会导致问题。

That's why it's generally a good idea when you're creating an immutable type to make it final as well - that way any consumers know that if they receive a reference with that type, it's definitely immutable.这就是为什么在创建不可变类型以使其成为final类型时通常是一个好主意的原因——这样任何消费者都知道,如果他们收到该类型的引用,它肯定是不可变的。

To put it another way: yes, the Abc class is immutable... but you can't assume that a reference with a compile-time type of Abc refers to an immutable object.换句话说:是的, Abc class 是不可变的……但是您不能假设具有Abc编译时类型的引用指的是不可变的 object。

As presented, yes, the class is immutable.如前所述,是的,class 是不可变的。

The "final" keyword on a class declaration prevents it from being extended - it's not related to immutability (unless your variables are declared public or protected). class 声明上的“final”关键字阻止它被扩展 - 它与不变性无关(除非您的变量被声明为公共或受保护的)。

Edit;编辑; "not related" is a poor choice of words, please see Jon Skeet's answer below “不相关”是一个糟糕的词选择,请参阅下面Jon Skeet 的回答

No , it is most likely not.,很可能不是。

A problem is terminology.一个问题是术语。 What do you mean by class ?你说的class是什么意思? If you mean this code , sure, it's immutable.如果你的意思是这段代码,当然,它是不可变的。 But 'this code' is not something that is particularly relevant to the concept of immutability.但是“这段代码”并不是与不变性概念特别相关的东西。 That usually makes a lot more sense if we consider it: this type .如果我们考虑一下,这通常会更有意义:这种类型

As in, is the type Abc immutable?例如, Abc类型是不可变的吗?

As in, given:如,给定:

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

is it safe to assume that the received abc couldn't possibly change?可以安全地假设收到的abc不可能改变吗?

And then the answer is no .然后答案是否定的。 That is not safe to assume: The type Abc is mutable .假设这是不安全的:类型Abcmutable

The reason is that someone could make this:原因是有人可以这样做:

class SneakyAbc extends Abc {
    private int id;

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

    public String getId() {
       return id;
    }
}

This is why immutable classes are virtually always made final , to fully guarantee it.这就是为什么不可变类几乎总是被设为final以完全保证它的原因。

Depending on how fancy you want to paint with the brush of 'what does this term mean', if all methods of Abc are final , you can consider it immutable as well if you really want to: Whilst the class does not need to be immutable (a subclass can add a new non-final field and create getters and setters for this), all the stuff you can 'witness' from the Abc type, assuming you don't use reflection, do appear immutable.取决于你想用“这个术语是什么意思”的画笔来画的花哨,如果 Abc 的所有方法都是final ,如果你真的想的话,你也可以认为它是不可变的:虽然 class 不需要是不可变的(子类可以添加一个新的非最终字段并为此创建 getter 和 setter),假设您不使用反射,您可以从 Abc 类型“见证”的所有内容确实看起来是不可变的。

Exactly what definition of immutable you go with would be required knowledge to delve any further. go 对不可变的确切定义需要进一步研究的知识。

Note that something like java.io.File has only final fields and is final, and yet, it has easily observable state that can be modified: just.. delete the file, and voila you can see it.请注意,像java.io.File类的东西只有最终字段并且是最终的,但是,它很容易观察到 state 可以修改。 You can pull similar stunts with an IdentityHashMap to create a faux but nevertheless very observable 'field'.您可以使用IdentityHashMap拉出类似的特技来创建一个虚假但非常可观察的“场”。

Thus, 'immutable' as a concept: Useful.因此,“不可变”作为一个概念:有用。 As a boolean flag to bestow upon a certain type or some java source file: Not useful.作为 boolean 标志以赋予某种类型或某些 java 源文件:无用。

Records记录

Other Answers directly addressed your questions about immutability, class being marked final , and subclasses being mutable.其他答案直接解决了您关于不变性的问题, class 被标记为final ,子类是可变的。 I'll add an alternative option to more briefly accomplish your goal of immutability: Records .我将添加一个替代选项来更简单地实现您的不变性目标: Records

Java 16 brings the new records feature. Java 16带来了新的记录功能。 If the main purpose of your class is to immutably and transparently carry data, define your class as a record.如果您的 class 的主要目的是不可变和透明地携带数据,请将您的 class 定义为记录。 The compiler implicitly creates the constructor, getters, equals & hashCode , and toString .编译器隐式创建构造函数、getter、 equals & hashCodetoString

A record is implicitly final , so no risk of a subclass becoming mutable.记录是隐含的final ,因此没有子类变得可变的风险。

Declare the properties in parentheses.在括号中声明属性。 By default, you need not put anything in the curly braces body of a record .默认情况下,您不需要在record的花括号主体中添加任何内容。

record Abc ( int id , String name ) {}

Instantiate like any other class.像任何其他 class 一样实例化。

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

The implicit getter methods are simply the property names.隐式 getter 方法只是属性名称。 The JavaBeans-style get… / is… method naming is not used.使用 JavaBeans 样式的get… / is…方法命名。 (You could add such methods if required.) (如果需要,您可以添加此类方法。)

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

Snuffleupagus Snuffleupagus

It is mutable if its internal states can be changed after the class is created.如果在创建 class 之后可以更改其内部状态,则它是可变的。

In your example, although there is no class final, the inside situations cannot be changed again because of final keyword.在您的示例中,虽然没有 class final,但由于 final 关键字,内部情况无法再次更改。 In this way, the class becomes immutable again这样,class 再次变为不可变

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM