使类不变的另一种方法

时间:2019-02-23 14:04:30

标签: java immutability

我正在阅读有效的Java第三版。主题是

  

第17项:最小化可变性

在本主题中,将讨论不可变对象,以及不使类最终化但仍将不可变的另一种方法。

以下是该主题中的内容:

  

回想一下,为了保证不变性,一个类必须不允许自己被子类化。这可以通过将类定为final来完成,但是还有另一个更灵活的选择。私有或打包私有,并添加公共静态工厂代替公共构造函数。

我可以同意将构造函数设为私有,这将使该类无法被子类化。但是带有包私有构造函数的类可以在包中被子类化。那么该类是否仍将与包私有构造函数保持不变?

编辑1:

带有包私有构造函数的类对于包外部的类仍然是不可变的。但是这种方法有用吗?

2 个答案:

答案 0 :(得分:1)

public class BaseClass {
   private final String arg1;
   private final String arg2;

   public BaseClass(final String arg1, final String arg2) {
      this.arg1 = arg1;
      this.arg2 = arg2;
   }

   public String getArg1() { return arg1; }
   public String getArg2() { return arg2; }
}

可以对此类进行子类化,但是必须始终提供arg1arg2的值,并且不能更改。显然,通过子类化,您可以覆盖两个getter方法。

我为避免我和同事可能犯的错误的方法是对interface(s)进行编程。

public interface BaseInterface {
   String arg1();
   String arg2();
}

在代码库中全面使用此接口可确保减少错误数量,因为内部状态可能仅在创建时更改。一旦对象脱离了创建点,就不能再对其进行更改(可以更改,但是您必须手动向下转换,这很容易发现)。


另一种避免子类化(以及可能的可变性)的策略是使用注释处理器。这将在编译时强制执行您的规则。

答案 1 :(得分:0)

具有包私有的构造函数不能保证100%不变,正如评论中已经提到的那样:

  

可以在同一包中创建可变子类

开发jar库时,您可能会在团队和软件包维护者之间达成一些协议,当然,这不能以编程方式强制执行,但是仍然在产品开发人员的控制之下。

那其他人呢?一个人可以在库的外部创建类,但是可以在同一包中以及在类路径中的多个位置中创建类。

如果您有这种情况,并且担心诸如此类的问题,Package Sealing就会发挥作用。

  

JAR文件中的包可以选择密封,这意味着   该软件包中定义的所有类都必须归档在同一JAR中   文件。您可能需要密封包装,例如,以确保版本   软件中各类之间的一致性。

     

如果要保证包中的所有类都来自   相同的代码源,使用JAR密封。密封的JAR指定所有   该JAR定义的程序包是密封的,除非在   每个包装。

假设您的jar库具有com.example.Factory成员的类protected/package-private,并且包com.example是密封的。尝试创建尝试访问这些成员的类com.example.Accessor的尝试将失败。

您可以通过这种方式来确保package-private个成员被有限(并且可能受信任)的成员组访问。

回到主要问题:

  

那么该类是否仍将与包私有构造函数保持不变?

维护者之间达成了强有力的协议,仔细的代码审查和封装密封可能会变得一成不变。