有两种解决方案,一种是函数式的,另一种是面向对象的(代码相同),线程安全,没有 "if",并且正确处理异常类型传播(这里没有任何解决方案可以处理这个问题)。
这段代码非常简短。更好的懒加载字段支持,由运行时处理,最终将使这段代码过时...
用法:
final Lazy<Integer, RuntimeException> lazyObject = new LazyField<>(() -> 1);
Lazy<Service, IOException> lazyFunc = lazyField(() -> this.lazyFunc = eval(new Service()));
final Lazy<Integer, RuntimeException> finalFunc = lazyField(() -> eval(1));
static Lazy<Integer, ServiceException> lazyTest = lazyField(() -> {throw new ServiceException();});
首先,我定义了一个带有异常的lambda:
@FunctionalInterface
interface SupplierWithException<T, E extends Exception> {
T get() throws E;
}
然后是一个Lazy类型:
interface Lazy<T, E extends Exception> extends SupplierWithException<T, E> {}
功能版本:
如果不像上面的示例那样用于最终字段,则直接返回一个lambda,最终获得更小的内存占用。
static <T, E extends Exception> Lazy<T, E> lazyField(Lazy<Lazy<T, E>, E> value) {
Objects.requireNonNull(value);
Lazy<T, E>[] field = new Lazy[1];
return () -> {
synchronized(field) {
if(field[0] == null)
field[0] = value.get();
return field[0].get();
}
};
}
static <T, E extends Exception> Lazy<T, E> eval(T value) {
return () -> value;
}
无法强制执行正确的值回调,至少它始终返回相同的结果,但可能无法避免“if”(如在最终字段情况下)。
对象版本:
对于外部来说是完全安全的。
public final class LazyField<T, E extends Exception> implements Lazy<T, E> {
private Lazy<T, E> value;
public LazyField(SupplierWithException<T, E> supplier) {
value = lazyField(() -> new Lazy<T, E>() {
volatile Lazy<T, E> memBarrier;
@Override
public T get() throws E {
value = memBarrier = eval(supplier.get());
}
});
}
@Override
public T get() throws E {
return value.get();
}
}
字段值的读取是无序的,但使用易失性memBarrier字段可以确保写入该字段的值的顺序。如果在惰性值被有效设置后调用初始lambda集,则该字段中设置的初始值也将返回初始化的惰性值。
享受
Lazy
呢? - rodolfino