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 b
Project Lombok provides a @Getter(lazy = true) annotation which does exactly what you need.
If you need something that approximates the behaviour of Lazy
in C#, which gives you thread safety and a guarantee that you always get the same value, there is no straightforward way to avoid if
.
You will need to use a volatile field and double checked locking. Here is the lowest memory footprint version of a class that gives you the C# behaviour:
public abstract class Lazy<T> implements Supplier<T> {
private enum Empty {Uninitialized}
private volatile Object value = Empty.Uninitialized;
protected abstract T init();
@Override
public T get() {
if (value == Empty.Uninitialized) {
synchronized (this) {
if (value == Empty.Uninitialized) {
value = init();
}
}
}
return (T) value;
}
}
It's not that elegant to use. You would have to create lazy values like this:
final Supplier<Baz> someBaz = new Lazy<Baz>() {
protected Baz init(){
return expensiveInit();
}
}
You can gain some elegance at the cost of additional memory footprint, by adding a factory method like this:
public static <V> Lazy<V> lazy(Supplier<V> supplier) {
return new Lazy<V>() {
@Override
protected V init() {
return supplier.get();
}
};
}
Now you can create thread safe lazy values simply like this:
final Supplier<Foo> lazyFoo = lazy(() -> fooInit());
final Supplier<Bar> lazyBar = lazy(() -> barInit());
final Supplier<Baz> lazyBaz = lazy(() -> bazInit());
How about this? then you can do something like this by using LazyInitializer
from Apache Commons: https://commons.apache.org/proper/commons-lang/javadocs/api-3.1/org/apache/commons/lang3/concurrent/LazyInitializer.html
private static Lazy<Double> _lazyDouble = new Lazy<>(()->1.0);
class Lazy<T> extends LazyInitializer<T> {
private Supplier<T> builder;
public Lazy(Supplier<T> builder) {
if (builder == null) throw new IllegalArgumentException();
this.builder = builder;
}
@Override
protected T initialize() throws ConcurrentException {
return builder.get();
}
}
Within your actual lambda, you can simply update the fooField
with a new lambda, such as:
class A<T>{
private Supplier<T> fooField = () -> {
T val = expensiveInit();
fooField = () -> val;
return val;
};
public T getFoo(){
return fooField.get();
}
}
Again this solution is not thread-safe as is the .Net Lazy<T>
, and does not ensure that concurrent calls to the getFoo
property return the same result.
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));
}
}
Stuart Mark's solution, with an explicit class. (Whether this is "better" is a personal preference thing, I think.)
public class ScriptTrial {
static class LazyGet<T> implements Supplier<T> {
private T value;
private Supplier<T> supplier;
public LazyGet(Supplier<T> supplier) {
value = null;
this.supplier = supplier;
}
@Override
public T get() {
if (value == null)
value = supplier.get();
return value;
}
}
Supplier<Integer> lucky = new LazyGet<>(()->seven());
int seven( ) {
return 7;
}
@Test
public void printSeven( ) {
System.out.println(lucky.get());
System.out.println(lucky.get());
}
}