我正在阅读有效的Java第三版。主题是
第17项:最小化可变性
在本主题中,将讨论不可变对象,以及不使类最终化但仍将不可变的另一种方法。
以下是该主题中的内容:
回想一下,为了保证不变性,一个类必须不允许自己被子类化。这可以通过将类定为final来完成,但是还有另一个更灵活的选择。私有或打包私有,并添加公共静态工厂代替公共构造函数。
我可以同意将构造函数设为私有,这将使该类无法被子类化。但是带有包私有构造函数的类可以在包中被子类化。那么该类是否仍将与包私有构造函数保持不变?
编辑1:
带有包私有构造函数的类对于包外部的类仍然是不可变的。但是这种方法有用吗?
答案 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; }
}
可以对此类进行子类化,但是必须始终提供arg1
和arg2
的值,并且不能更改。显然,通过子类化,您可以覆盖两个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
个成员被有限(并且可能受信任)的成员组访问。
回到主要问题:
那么该类是否仍将与包私有构造函数保持不变?
维护者之间达成了强有力的协议,仔细的代码审查和封装密封可能会变得一成不变。