2015-01-05 73 views
3

對於商業決策應用程序,我遇到了很多情況,我必須緩存一個昂貴的值,並使用延遲初始化。所以我利用泛型和供應商lambda來封裝一個懶惰的初始化。在懶惰的初始化供應商中引用「this」?

import java.util.function.Supplier; 

public final class LazyProperty<T> { 
    private final Supplier<T> supplier; 
    private volatile T value; 

    private LazyProperty(Supplier<T> supplier) { 
     this.supplier = supplier; 
    } 
    public T get() { 
     if (value == null) { 
      synchronized(this) { 
       if (value == null) { 
        value = supplier.get(); 
       } 
      } 
     } 
     return value; 
    } 

    public static <T> LazyProperty<T> forSupplier(Supplier<T> supplier) { 
     return new LazyProperty<T>(supplier); 
    } 
} 

但我希望能夠在我不能初始化屬性的對象被創建後,直到情況下也用這個,因爲在創建後它的對象只能算這個屬性(通常需要自身或其他對象的上下文)。但是,這通常需要在供應商功能中參考this

public class MyClass { 
    private final LazyProperty<BigDecimal> expensiveVal = 
     LazyProperty.forSupplier(() -> calculateExpensiveVal(this)); 

    public BigDecimal getExpensiveVal() { 
     return expensiveVal.get(); 
    } 
} 

只要我能保證MyClass構造(通過getExpensiveVal()方法)後LazyProperty的get()功能只叫,不應該是由於供應商的this參考任何部分建設問題,正確的?

+0

我不一定看到這個問題,因爲'this'是指'MyClass'而不是lambda。你是否遇到過這個問題,還是擔心它在概念上不合理? – Makoto

+0

不,我沒有問題,是的,我只是關心這在概念上是不健全的。我一直認爲在初始化或構造中引用'this'是有風險的,因爲它可以暴露一個部分構造的對象。但對於函數式編程,任何包含'this'引用的lambda表達式(指MyClass)都是無害的,除非它們在構造時被執行。 – tmn

+1

是的;這就是爲什麼我在這裏看不到問題。你在任何地方都沒有在構造函數中使用'expensiveVal'字段,所以我不相信你會遇到一個問題。您可能會對類型安全性感興趣,因爲您必須確保'calculateExpensiveVal'將使用您想要的正確類型。 – Makoto

回答

1

您的代碼將有一個問題取決於執行方法calculateExpensiveVal

  1. 如果calculateExpensiveVal上的MyClass通過引用您將獲得NullPointerException異常調用getExpensiveVal。

  2. 如果calculateExpensiveVal創建一個線程,並通過MyClass的的基準,再次你可能會碰到同樣的問題點1

但是,如果你能保證calculateExpensiveVal沒有做任何的事情,那麼你的代碼在線程安全透視中是正確的。 MyClass的永遠不會出現部分構造 因爲由JMM

提供的最終 gaurantees的說法之後,即使你的* calculateExpensiveVal可以使用任何一種或兩種這些你只打算有問題getExpensiveVal點方法與NullPointerException。

您的lazyProperty.get方法已經線程安全,所以有任何問題。

因爲你總會看到完全構造,因爲final關鍵字的供應商對象(只有當你沒有逃過「此」參考到另一個線程),並且您已經使用揮發性的場這需要關心看完全構造的對象。

+0

當然。我可能可以讓我的抽象例子更像SSCCE,但我絕對同意你的觀點和多線程濫用的可能性。 – tmn

2

基於小碼你表明你不應該有任何問題,但我可能會寫你的類像這樣更加明確:

public class MyClass { 
    private final LazyProperty<BigDecimal> expensiveVal; 

    public MyClass() { 
     this.expensiveVal = LazyProperty.forSupplier(() -> calculateExpensiveVal(MyClass.this)); 
    } 

    public BigDecimal getExpensiveVal() { 
     return expensiveVal.get(); 
    } 
}