2 solutions, one functional and then one object (this is the same code), thread-safe , without an "if" and take care of handling exceptions with the correct type propagation (there is no solution to take care of this).
This is pretty short. The best lazy field support handled by the runtime will ultimately make this code obsolete ...
using:
// 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 and one "if". null check removal may remove the "if"... 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 the exception of:
@FunctionalInterface interface SupplierWithException<T, E extends Exception> { T get() throws E; }
Then the lazy type:
interface Lazy<T, E extends Exception> extends SupplierWithException<T, E> {}
Functional Version:
It directly returns a lambda, which ultimately gets less memory if not used in the final field, as in the example 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 () -> { if(field[0] == null) { 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; }
You cannot be forced to give the correct value for the callback, at least it always returns the same result, but cannot avoid the "if" (as in the latter case of the field).
Object Version:
It is completely safe 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(() -> value = eval(supplier.get())); } @Override public T get() throws E { return value.get(); } }
enjoy