依赖注入,初始化后的不变性

时间:2010-05-25 20:19:01

标签: java dependency-injection language-design immutability

我希望能够指定一个对象的成员变量在对象被“初始化”后是不可变的,这对我来说意味着在它注入任何依赖项之后,并且已经执行了任何其他初始化操作只能在DI之后执行。

是否有满足我兴趣的语言 - 以这种方式形式化DI,初始化和支持不变性?也许让它们成为语言的一部分是愚蠢的;也许不吧。我不确定。

今天我用Java编程,但我不能像我想的那样使用“final”,因为这些阶段在构造函数完成执行后发生。关于如何通过Java获得我想要的任何建议?我想我可以让我的对象实现一个基类,以便在构造函数完成之前发生这些阶段,或者使用方面来执行相同的操作。

思想?

5 个答案:

答案 0 :(得分:4)

生成不可变对象有两种主要方式:

  1. 使用构建器/工厂模式 - 构建器可能是可变的,但它创建的对象是不可变的,通常用最终字段实现。您还可以将两者结合起来,因此对象本身用于构建新实例,通常通过“mutator”方法在单独的新实例上更改状态。 Spring的FactoryBean就是一个例子。

  2. 创建一个MutableObject子类,它维护一个可变状态的标志。在进行任何更改之前,所有mutators都检查可变状态 - 如果对象已设置为immutable,则检查会抛出异常,否则更改将继续。

  3. 第一种方法是以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的一个片段:

  

这必然意味着所有   公共领域是最终的,而且都是最终的   公共最终参考字段是指   其他不可变对象