我希望能够指定一个对象的成员变量在对象被“初始化”后是不可变的,这对我来说意味着在它注入任何依赖项之后,并且已经执行了任何其他初始化操作只能在DI之后执行。
是否有满足我兴趣的语言 - 以这种方式形式化DI,初始化和支持不变性?也许让它们成为语言的一部分是愚蠢的;也许不吧。我不确定。
今天我用Java编程,但我不能像我想的那样使用“final”,因为这些阶段在构造函数完成执行后发生。关于如何通过Java获得我想要的任何建议?我想我可以让我的对象实现一个基类,以便在构造函数完成之前发生这些阶段,或者使用方面来执行相同的操作。
思想?
答案 0 :(得分:4)
生成不可变对象有两种主要方式:
使用构建器/工厂模式 - 构建器可能是可变的,但它创建的对象是不可变的,通常用最终字段实现。您还可以将两者结合起来,因此对象本身用于构建新实例,通常通过“mutator”方法在单独的新实例上更改状态。 Spring的FactoryBean
就是一个例子。
创建一个MutableObject子类,它维护一个可变状态的标志。在进行任何更改之前,所有mutators都检查可变状态 - 如果对象已设置为immutable,则检查会抛出异常,否则更改将继续。
第一种方法是以Spring为中心的,因为它需要一个特定于弹簧的接口。您可以通过bean上的factory-method
/ factory-bean
属性创建常规bean的工厂bean,从而消除代码中的spring依赖关系。
使用第二种方法对弹簧特别有用。你可以指示spring在bean初始化之后调用一个方法,例如seal()密封对象 - 使其不可变。或者,您可以实现一个小BeanFactoryPostProcessor
来自动执行此操作,而无需记住设置init-method =“seal”。在每个不可变的bean上。
答案 1 :(得分:3)
我想这取决于你想要的不变性。如果你想要保证线程安全(必须将所有内容都声明为final,包括依赖项),那么我认为工厂,构建器或构造函数注入是你唯一的选择。
如果你只想要状态的不变性,那么声明状态变量final应该就足够了。即使是不可变的String类在其实现中也有一个可变字段(哈希码值的缓存)。只要您的代码以其他方式确保实例在没有注入的情况下不可用,那么一切都应该很好。
答案 2 :(得分:1)
在Java中,您可以使用builder在其构造函数中初始化不可变对象,这样您就可以避开setter。
但是,如果您使用Scala,则不变性为the default。
答案 3 :(得分:0)
在Java中,如果你无论如何使用mutator方法进行设置,那么在初始化对象后添加逻辑以防止更改是非常便宜的(虽然在我看来也很难看)。
public void setMyProperty(String newValue) {
checkInitialized();
myProperty = newValue;
}
public void checkInitialized() {
if ( initialized ) {
throw new IllegalStateException("Setter called after initialization");
}
}
充其量虽然这是动态检查。它没有给你任何关于你已有的静态反馈。
答案 4 :(得分:0)
要指定类是不可变的,可以使用@Immutable
注释。
您可以看到Javadoc here。
这适用于Eclipse中的Findbugs插件。
@Alexander:
据我所知,他问如何指定一个类是不可变的。不是如何编写不可变类。此注释可以为您提供工具支持,以验证您声明的类中没有您声称不可变的错误。
Javadoc的一个片段:
这必然意味着所有 公共领域是最终的,而且都是最终的 公共最终参考字段是指 其他不可变对象