在Java8中模拟Lazy

sha*_*pir 5 java evaluation lambda lazy-evaluation java-8

我编写了以下代码来Lazy<T>在Java中进行模拟:

import java.util.function.Supplier;

public class Main {

    @FunctionalInterface
    interface Lazy<T> extends Supplier<T> {
        Supplier<T> init();
        public default T get() { return init().get(); }
    }

    static <U> Supplier<U> lazily(Lazy<U> lazy) { return lazy; }
    static <T>Supplier<T> value(T value) { return ()->value; }

    private static Lazy<Thing> thing = lazily(()->thing=value(new Thing()));
    public static void main(String[] args) {

        System.out.println("One");
        Thing t = thing.get();
        System.out.println("Three");

    }
    static class Thing{ Thing(){System.out.println("Two");}}
}
Run Code Online (Sandbox Code Playgroud)

但我得到以下警告:

" (T)Main中的值不能应用于(com.company.Main.Thing)原因:没有类型变量T的实例存在,因此Supplier<T> 符合Lazy<Thing>"

你能帮我找出问题所在吗?提前致谢!

Rom*_*kiy 1

value()返回 a Supplier,而thing字段的类型为Lazy<Thing>。您不能将 a 分配Supplier给 a Lazy(使用任何参数化),因为并非所有Supplier实例都是Lazy实例。

此外,尝试将lazily()返回值(即 a Supplier)分配给thing,但出于同样的原因,这不会起作用。

我们可以将lazily类型更改为Lazy并删除该内联thing=赋值(位于thing初始值设定项表达式内)以使其编译:

static <U> Lazy<U> lazily(Lazy<U> lazy) { return lazy; }
static <T> Supplier<T> value(T value) { return ()->value; }

private static Lazy<Thing> thing = lazily(()->value(new Thing()));
Run Code Online (Sandbox Code Playgroud)

但我不确定这是否是你想要的。

如果您只是想要一种惰性行为,Supplier它本身已经能够表现出惰性,因为get()仅在请求时执行,而不是在Supplier创建时执行。

如果你想要一个缓存逻辑(只计算一次,并且只计算需要的),你可以使用这样的东西:

public class CachingSupplier<T> implements Supplier<T> {
    private final Supplier<T> supplier;
    private T cachedValue;
    private boolean computed = false;

    public CachingSupplier(Supplier<T> supplier) {
        this.supplier = supplier;
    }

    public T get() {
        if (!computed) {
            cachedValue = supplier.get();
            computed = true;
        }
        return cachedValue;
    }
}
Run Code Online (Sandbox Code Playgroud)

如果你想保证supplier.get()最多被调用一次,你可以应用一些同步:

    if (!computed) {
        synchronized (this) {
            if (!computed) {
                cachedValue = supplier.get();
                computed = true;
            }
        }
    }
    return cachedValue;
Run Code Online (Sandbox Code Playgroud)

这里,使用了双重检查锁定。