2010-11-09 76 views
4

我一直在使用scala的lazy val成语,我想在Java中实现类似的东西。我的主要问题是,为了构造一些价值,我需要一些在对象构建时不知道的其他值,但我不想在之后改变它。原因在于,我使用一个GUI库,以我的名义为对象创建实例,并在创建我需要的所有内容时调用其他方法,这是我知道需要的值的时候。推迟初始化不可变变量

以下是我尝试实现的属性:
*我的变量的不变性。
*以构造函数以外的其他方法初始化。

我不认为这是可能的在Java中,因为只有final实现变量的不变性和final变量不能在构造函数之外初始化。

Java中最接近我想达到的目标是什么?

回答

5

这样做的一种方法是将有问题的值的实际实例化推送到另一个类中。这将是最终的,但在类加载之前不会实际创建,这会延迟到需要时才会被创建。类似以下内容:

public class MyClass 
{ 
    private static class Loader 
    { 
     public static final INSTANCE = new Foo(); 
    } 

    Foo getInstance() 
    { 
     return Loader.INSTANCE; 
    } 
} 

这会在需要时懒惰地初始化Foo

如果你绝对需要Foo作为你的顶级课程的实例变量 - 我想不出任何方式来做到这一点。如您所述,变量必须在构造函数中填充

其实我不知道斯卡拉究竟是如何得到解决这个问题,但我的猜测是,这台lazy val变量某种这是由实际的对象替换第一评估时。在这种情况下,Scala当然可以通过颠覆正常访问修饰符来实现,但我不认为您可以透明地在Java中执行此操作。您可以宣布该字段为例如一个Future<Foo>它在第一次调用时创建值并从该点缓存它,但这不是透明地透明的,并且根据final的定义我没有看到解决方法。

1

Andrzej's answer很好,但也有一种方法可以在不更改源代码的情况下执行此操作。使用AspectJ捕获构造函数调用和返回未初始化的对象:

pointcut lazyInit() : execution(* com.mycompany.expensiveservices.*.init(*)); 

void around() : lazyInit() && within(@Slow *) { 

    new Thread(new Runnable(){ 

     @Override 
     public void run(){ 
      // initialize Object in separate thread 
      proceed(); 
     } 
    } 
} 

考虑到这方面,标有@Slow注释对象的所有构造函数将在一个单独的线程中运行。

我没有找到很多关于链接的参考,但请通过Ramnivas Laddad了解更多信息,请阅读AspectJ in Action