不可变变量的延迟初始化
我经常使用scala的lazy val
习惯用法,我想在Java中实现类似的东西。我的主要问题是,要构造一些值,我需要一些在对象构造时未知的其他值,但我不希望之后能够更改它。原因是,我正在使用一个 GUI 库,它代表我实例化对象,并在创建我需要的所有内容时调用不同的方法,即当我知道我需要的值时。
以下是我尝试实现的属性:
* 我的变量的不变性。
* 在构造函数之外的其他方法中进行初始化。
我认为这在 Java 中是不可能的,因为只有 final
实现了变量的不变性,并且 final
变量不能在构造函数之外初始化。
Java 中与我想要实现的目标最接近的是什么?
I've been using scala's lazy val
idiom a lot and I would like to achieve something similar in Java. My main problem is that to construct some value I need some other value which is not known at object construction time, but I do not want to be able to change it afterwards. The reason for that is, I'm using a GUI library which instanciates the object on my behalf and calls a different method when everything I need is created, which is when I know the values I need.
Here are the properties I try to achieve:
* Immutability of my variable.
* Initialization in some other method than the constructor.
I do not think this is possible in Java, for only final
achieves immutability of the variable and final
variables cannot be initialized outside of the constructor.
What would be the closest thing in Java to what I am trying to achieve ?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
一种方法是将相关值的实际实例化推入另一个类。这将是最终的,但在加载类之前不会实际创建,而加载类会推迟到需要时为止。如下所示:
这将在需要时延迟初始化
Foo
。如果您绝对需要 Foo 成为顶级类的实例变量 - 我想不出任何即兴的方法来做到这一点。正如您所指出的,变量必须填充在构造函数中。
事实上,我不确定 Scala 是如何解决这个问题的,但我的猜测是,它将 lazy val 变量设置为某种 thunk ,该变量被替换为第一次评估时的实际对象。在这种情况下,Scala 当然可以通过颠覆正常的访问修饰符来做到这一点,但我认为您不能在 Java 中透明地做到这一点。您可以将该字段声明为
Future
,它在第一次调用时创建值并从该点开始缓存它,但这不是引用透明的,并且根据final 的定义
我没有找到解决这个问题的方法。One way to do it would be to push the actual instantiation of the value in question into another class. This will be final, but won't be actually created until the class is loaded, which is deferred until it is needed. Something like the following:
This will lazily initialise the
Foo
as and when desired.If you absolutely need the
Foo
to be an instance variable of your top-level class - I can't think of any way off-hand to do this. The variable must be populated in the constructor, as you noted.In fact I'm not sure exactly how Scala gets around this, but my guess would be that it sets the
lazy val
variable to some kind of thunk which is replaced by the actual object when first evaluated. Scala can of course do this by subverting the normal access modifiers in this case, but I don't think you can transparently do this in Java. You could declare the field to be e.g. aFuture<Foo>
which creates the value on first invocation and caches it from that point on, but that's not referentially transparent, and by the definition offinal
I don't see a way around this.Andrzej 的答案很棒,但还有一种方法可以做到这一点无需更改源代码。使用 AspectJ 捕获构造函数调用并返回未初始化的对象:
给定此方面,对象的所有构造函数标有@Slow注解的将在单独的线程中运行。
我没有找到太多参考链接,但请阅读 AspectJ in Action by Ramnivas Laddad 了解更多信息。
Andrzej's answer is great, but there is also a way to do it without changing the source code. Use AspectJ to capture Constructor invocations and return non-initialized objects:
Given this aspect, all constructors of objects marked with a
@Slow
annotations will be run in a separate thread.I did not find much reference to link to, but please read AspectJ in Action by Ramnivas Laddad for more info.