Last active
August 29, 2015 13:56
-
-
Save Ramasubramanian/9052370 to your computer and use it in GitHub Desktop.
A toned down implementation of C sharp's Lazy in Java 8
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package in.raam.lazy; | |
import java.io.Serializable; | |
import java.util.Optional; | |
import java.util.concurrent.atomic.AtomicBoolean; | |
import java.util.concurrent.atomic.AtomicReference; | |
import java.util.concurrent.locks.ReentrantLock; | |
import java.util.function.Supplier; | |
/** | |
* Class to lazily initialize a value based on provided Factory method or | |
* class's default constructor inspired from C# Lazy<T> using Lambda and | |
* Method reference capabilities of Java 8. All exceptions resulting from | |
* factory methods are cascaded to caller. Exceptions from default constructor | |
* is wrapped as a RuntimeException. Throws NullPointerException if the factory | |
* method itself is null or returns null on execution to fail fast. Available as | |
* both Thread safe(default) and unsafe versions. Usage examples | |
* | |
* <pre> | |
* public Lazy<IntensiveResource> r = Lazy.create(IntensiveResource.class, false); | |
* | |
* public Lazy<IntensiveResource> r1 = Lazy.create(IntensiveResource::buildResource); | |
* | |
* public Lazy<IntensiveResource> r2 = Lazy.create(() -> return new IntensiveResource()); | |
* </pre> | |
* | |
* Invoking toString() will cause the object to be initialized. Accessing the | |
* value of the Lazy object using Lazy.value() method causes the object to | |
* initialize and execute the Factory method supplied. Values can also be | |
* obtained as {@link java.util.Optional} to avoid NPEs | |
* | |
* @author raam | |
* | |
* @param <V> | |
* - Type of the object obtained after deferred creation | |
*/ | |
public class Lazy<V> implements Serializable { | |
private static final long serialVersionUID = 1L; | |
private static final String FACTORY_NULL_RETURN_MESSAGE = "Factory method returns null for Lazy value"; | |
private V value; | |
private Boolean created = Boolean.FALSE; | |
protected Supplier<V> factory; | |
public static <V> Lazy<V> create(Class<V> valueClass, boolean threadSafe) { | |
if (threadSafe) { | |
return new ThreadSafeLazy<V>(valueClass); | |
} else { | |
return new Lazy<V>(valueClass); | |
} | |
} | |
public static <V> Lazy<V> create(Class<V> valueClass) { | |
return create(valueClass, true); | |
} | |
public static <V> Lazy<V> create(Supplier<V> factoryMethod, boolean threadSafe) { | |
if (threadSafe) { | |
return new ThreadSafeLazy<V>(factoryMethod); | |
} else { | |
return new Lazy<V>(factoryMethod); | |
} | |
} | |
public static <V> Lazy<V> create(Supplier<V> factoryMethod) { | |
return create(factoryMethod, true); | |
} | |
private Lazy(Class<V> valueClass) { | |
factory = () -> { | |
try { | |
return valueClass.newInstance(); | |
} catch (Exception e) { | |
throw new RuntimeException(e); | |
} | |
}; | |
} | |
private Lazy(Supplier<V> factoryMethod) { | |
if (factoryMethod == null) { | |
throw new NullPointerException(); | |
} | |
this.factory = factoryMethod; | |
} | |
public Boolean created() { | |
return created; | |
} | |
public V value() { | |
if (!created()) { | |
initialize(); | |
if (getValue0() == null) { | |
throw new NullPointerException(FACTORY_NULL_RETURN_MESSAGE); | |
} | |
} | |
return getValue0(); | |
} | |
protected V getValue0() { | |
return value; | |
} | |
public Optional<V> optional() { | |
if (!created()) { | |
initialize(); | |
} | |
return Optional.ofNullable(getValue0()); | |
} | |
protected void initialize() { | |
value = factory.get(); | |
created = Boolean.TRUE; | |
} | |
@Override | |
public String toString() { | |
return value().toString(); | |
} | |
private static class ThreadSafeLazy<V> extends Lazy<V> { | |
private static final long serialVersionUID = 1L; | |
private AtomicBoolean created = new AtomicBoolean(false); | |
private AtomicReference<V> value = new AtomicReference<V>(); | |
private ReentrantLock writeLock = new ReentrantLock(); | |
private ThreadSafeLazy(Class<V> valueClass) { | |
super(valueClass); | |
} | |
private ThreadSafeLazy(Supplier<V> factoryMethod) { | |
super(factoryMethod); | |
} | |
@Override | |
public Boolean created() { | |
return created.get(); | |
} | |
@Override | |
protected V getValue0() { | |
return value.get(); | |
} | |
@Override | |
protected void initialize() { | |
writeLock.lock(); | |
try { | |
if (value.get() == null) { | |
if (value.compareAndSet(null, factory.get())) { | |
created.set(true); | |
} | |
} | |
} finally { | |
writeLock.unlock(); | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
What is the license type of this file? I couldn't find it anywhere. Please advice