为什么在Java中声明一个不可变的类最终结果?

2022-08-31 11:06:05

我读到,为了使一个类在Java中不可变,我们应该执行以下操作,

  1. 不提供任何设置器
  2. 将所有字段标记为私有
  3. 使课程最终确定

为什么需要步骤 3?为什么要标记类?final


答案 1

如果你不标记类,我可能会突然把你看似不可变的类变成可变的。例如,请考虑以下代码:final

public class Immutable {
     private final int value;

     public Immutable(int value) {
         this.value = value;
     }

     public int getValue() {
         return value;
     }
}

现在,假设我执行以下操作:

public class Mutable extends Immutable {
     private int realValue;

     public Mutable(int value) {
         super(value);

         realValue = value;
     }

     public int getValue() {
         return realValue;
     }
     public void setValue(int newValue) {
         realValue = newValue;
     }

    public static void main(String[] arg){
        Mutable obj = new Mutable(4);
        Immutable immObj = (Immutable)obj;              
        System.out.println(immObj.getValue());
        obj.setValue(8);
        System.out.println(immObj.getValue());
    }
}

请注意,在我的子类中,我已覆盖读取子类中声明的新可变字段的行为。因此,您的类(最初看起来不可变)实际上并不是不可变的。我可以将此对象传递到任何预期的对象,这可能会对代码造成非常糟糕的事情,假设该对象确实是不可变的。标记基类可防止这种情况发生。MutablegetValueMutableImmutablefinal

希望这有帮助!


答案 2

与许多人所认为的相反,制作一个不可变的类并不是必需的。final

创建不可变类的标准参数是,如果不这样做,则子类可以添加可变性,从而违反超类的协定。该类的客户端将假定不可变性,但是当某些东西从它们下面变异出来时,他们会感到惊讶。final

如果你把这个参数带到它的逻辑极端,那么所有的方法都应该被提出来,否则子类可能会以不符合其超类契约的方式覆盖一个方法。有趣的是,大多数Java程序员认为这是荒谬的,但不知何故,他们对不可变类应该是的想法是可以接受的。我怀疑这与Java程序员通常对不可变性的概念并不完全满意有关,也许是与Java中关键字的多种含义有关的某种模糊思维。finalfinalfinal

遵守超类的契约不是编译器可以或应该始终强制执行的。编译器可以强制执行协定的某些方面(例如:一组最小方法及其类型签名),但编译器无法强制执行典型协定的许多部分。

不可变性是类协定的一部分。这与人们更习惯的一些事情有点不同,因为它说明了类(以及所有子类)不能做什么,而我认为大多数Java(通常是OOP)程序员倾向于认为契约与类可以做什么有关,而不是它不能做什么。

不可变性还影响的不仅仅是单个方法 -它会影响整个实例 - 但这与 Java 的工作方式和工作方式并没有太大区别。这两种方法在 中列出了特定的合同。该合同非常仔细地列出了这些方法无法做到的事情。此合约在子类中更具体。很容易覆盖或以违反合同的方式覆盖。实际上,如果您只覆盖这两种方法中的一种而没有另一种方法,则很可能违反了合同。那么应该并且已经宣布以避免这种情况吗?我想大多数人会争辩说他们不应该。同样,也没有必要创建不可变的类 。equalshashCodeObjectequalshashCodeequalshashCodefinalObjectfinal

也就是说,您的大多数类,无论是否可变,可能都应该是 .请参阅《有效的 Java 第二版》第 17 项:“用于继承的设计和文档,否则将禁止继承”。final

因此,步骤 3 的正确版本是:“使类成为最终类,或者在设计子类化时,清楚地记录所有子类必须继续是不可变的。