2010-11-09 87 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