2 solutions, one functional then and one object (it's same code), thread safe, without "if", and taking care of Exception handling with proper type propagation (no solution here take care about that).
It is quite short. Better lazy fields support, handled by the runtime, will eventually make this code obsolete...
usage :
// object version : 2 instances (object and lambda)
final Lazy<Integer, RuntimeException> lazyObject = new LazyField<>(() -> 1);
// functional version : more efficient than object, 1 instance
// usage : wrap computed value using eval(arg), and set the lazy field with result
Lazy<Service, IOException> lazyFunc = lazyField(() -> this.lazyFunc = eval(new Service()));
// functional final version, as field is final this is less efficient than object :
// 2 instances one "if" and one sync (that could still be avoided...)
final Lazy<Integer, RuntimeException> finalFunc = lazyField(() -> eval(1));
// Here the checked exception type thrown in lambda can only be ServiceException
static Lazy<Integer, ServiceException> lazyTest = lazyField(() -> {throw new ServiceException();});
First I define a lambda with exception :
@FunctionalInterface
interface SupplierWithException<T, E extends Exception> {
T get() throws E;
}
Then a Lazy type :
interface Lazy<T, E extends Exception> extends SupplierWithException<T, E> {}
Functional version :
It directly returns a lambda that eventually get the less memory footprint, if not used on a final field like in sample above.
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;
}
One can not enforce to give a correct value callback, at least it always returns the same result but may not avoid the "if" (as in final field case).
Object version :
Is fully safe from the outside.
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();
}
}
the read of field value is unordered, but use of volatile memBarrier field ensure ordering of value written in this field. The initial lambda set in this field will also returns initialized lazy value if called after the lazy value was effectively set.
enjoy
Lazy
equivalent in Java? – Condescend