Referencing "this" in a lazy init provider?

For business solution applications, I face many cases where I have to cache an expensive value with lazy initialization. So I used Generics and Vendor lambda to encapsulate lazy initialization.

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);
    }
}

      

But I would like to be able to use this also in cases where I cannot initialize the property until the object is created, because the object can only compute this property after it has been created (usually this is necessary for the context itself or others objects). However, this often requires a reference to this

in the provider function.

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

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

      

As long as I can guarantee that the LazyProperty function get()

is only called after it has been created MyClass

(using a method getExpensiveVal()

), there shouldn't be any problem with the partial construct due to the reference this

to the provider, right?

+3


source to share


2 answers


There will be one problem in your code which depends on the implementation of the calculateExpensiveVal method .

  • if calculateExpensiveVal calls getExpensiveVal on the passed in MyClass reference, you will get a NullPointerException.

  • if calculateExpensiveVal creates a stream and passes the reference to MyClass, again you might run into the same problem as in point 1.

But if you guarantee that calculateExpensiveVal does nothing, then your code will be correct from a security perspective. MyClass will never be viewed in part due to the final gaurantees provided by JMM



After you said that even if your * calculation of ExpensiveVal can use either or both of these points, you only have a problem with the getExpensiveVal method with a NullPointerException.

your lazyProperty.get method is already thread safe, so there was no problem.

Because you will always see a fully constructed provider object because of the final keyword ( unless you escaped the 'this' reference to another thread ) and you have already used volatile for the valuewhich takes care of looking at the fully constructed value object .

+1


source


Based on the little code you showed, you shouldn't have any problems, but I would probably write your class like this:



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

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

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

      

+2


source







All Articles