为什么最终关键字对于不可变类是必需的?

2022-09-02 01:10:34

您能否澄清一下,为什么当我们将其设置为不可变关键字时,在class之前需要最终关键字。我的意思是,如果我们声明它的所有属性都是私有的和最终的,那么它也是一个不可变的类,不是吗?

抱歉,如果这个问题看起来很容易,但我真的对此感到困惑。帮帮我。

已编辑:我知道声明为 final 的类不能被子类化。但是,如果每个属性都是私有的和最终的,那么这有什么区别呢?


答案 1

正如 stacker 所说,确保类没有被子类化。这很重要,这样任何依赖于其不变性的代码都可以安全地做到这一点。final

例如,不可变类型(其中每个字段也是不可变类型)可以在线程之间自由使用,而不必担心数据争用等。现在考虑:

public class Person {
    private final String name;

    public Person(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

看起来您可以跨线程自由共享实例,而不会出现任何问题。但是,当您共享的对象实际上是可变子类时,该怎么办:Person

public class Employee extends Person {
    private String company;

    public Employee(String name, String company) {
        super(name);
        this.company = company;
    }

    public void setCompany(String company) {
        this.company = company;
    }

    public String getCompany() {
        return company; 
    }
}

现在,在线程之间共享 的实例并不安全,因为它们不是不可变的。但是执行共享的代码可能只知道它们作为...引导他们进入虚假的安全感。EmployeePerson

缓存也是如此 - 缓存和重用不可变类型应该是安全的,对吧?好吧,缓存真正属于不可变类型的实例安全的 - 但是如果您正在处理的类型本身不允许突变,但确实允许子类,那么它突然不再安全。

想想.它没有任何可变字段,但将每个引用视为对不可变类型的引用显然是一个坏主意。基本上,这取决于你是否将不可变性视为类型或对象的属性。一个真正不可变的类型声明“任何时候你看到这种类型的引用,你都可以将其视为不可变的” - 而允许任意子类化的类型不能提出这个声明。java.lang.ObjectObject

顺便说一句,有一个半途而废的房子:如果你能将子类化限制在“可信”的地方,你可以确保一切都是不可变的,但仍然允许该子类化。Java中的访问使这变得棘手,但是例如在C#中,您可以拥有一个仅允许在同一程序集内进行子类化的公共类 - 提供一个在不变性方面很好且很强的公共API,同时仍然允许多态性的好处。


答案 2

不能对声明为 final 的类进行子类化。另请参见 http://docs.oracle.com/javase/tutorial/java/IandI/final.html

最终关键字的所有用法的不同语义在 Java 语言规范中进行了描述

  • 4.12.4 最终变量 第 80 页
  • 8.1.1.2 最终类 第184页
  • 8.3.1.2 最终字段 第 209 页
  • 8.4.3.3 最终方法 第223页