Lazy field initialization with lambdas

2019-01-07 12:47发布

I would like to implement lazy field initialization (or deferred initialization) without an if statement and taking advantage of lambdas. So, I would like to have the same behavior of the following Foo property but without the if:

class A<T>{
    private T fooField;

    public T getFoo(){
        if( fooField == null ) fooField = expensiveInit();
        return fooField;
    }
}

Ignore the fact that this solution is not guaranteeing safe use for: 1) multi-threading; 2) null as a valid value of T.

So, to express the intention that the initialization of the fooField is deferred until its first use I would like to declare the fooField of the type Supplier<T> such as:

class A<T>{
   private Supplier<T> fooField = () -> expensiveInit();

   public T getFoo(){
      return fooField.get();
   }
}

and then in the getFoo property I would just return fooField.get(). But now I want that next invocations to getFoo property avoid the expensiveInit() and just return the previous T instance.

How can I achieve that without using an if?

Despite naming conventions and replacing the ->by =>, then this example could be also considered in C#. However, NET Framework version 4 already provides a Lazy<T> with the desired semantics.

13条回答
闹够了就滚
2楼-- · 2019-01-07 13:49

Here's a solution using Java's Proxy (reflection) and Java 8 Supplier.

* Because of the Proxy usage, the initiated object must implement the passed interface.

* The difference from other solutions is the encapsulation of the initiation from the usage. You start working directly with DataSource as if it was initialized. It will be initialized on the first method's invocation.

Usage:

DataSource ds = LazyLoadDecorator.create(() -> initSomeDS(), DataSource.class)

Behind the scenes:

public class LazyLoadDecorator<T> implements InvocationHandler {

    private final Object syncLock = new Object();
    protected volatile T inner;
    private Supplier<T> supplier;

    private LazyLoadDecorator(Supplier<T> supplier) {
        this.supplier = supplier;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (inner == null) {
            synchronized (syncLock) {
                if (inner == null) {
                    inner = load();
                }
            }
        }
        return method.invoke(inner, args);
    }

    protected T load() {
        return supplier.get();
    }

    @SuppressWarnings("unchecked")
    public static <T> T create(Supplier<T> supplier, Class<T> clazz) {
        return (T) Proxy.newProxyInstance(LazyLoadDecorator.class.getClassLoader(),
                new Class[] {clazz},
                new LazyLoadDecorator<>(supplier));
    }
}
查看更多
登录 后发表回答