package edu.stanford.nlp.util;
import java.lang.ref.SoftReference;
import java.util.function.Supplier;
/**
* An instantiation of a lazy object.
*
* @author Gabor Angeli
*/
public abstract class Lazy<E> {
/** If this lazy should cache, this is the cached value. */
private SoftReference<E> implOrNullCache = null;
/** If this lazy should not cache, this is the computed value */
private E implOrNull = null;
/** For testing only: simulate a GC event. */
void simulateGC() {
if (implOrNullCache != null) {
implOrNullCache.clear();
}
}
/**
* Get the value of this {@link Lazy}, computing it if necessary.
*/
public synchronized E get() {
E orNull = getIfDefined();
if (orNull == null) {
orNull = compute();
if (shouldGC()) {
implOrNullCache = new SoftReference<>(orNull);
} else {
implOrNull = orNull;
}
}
assert orNull != null;
return orNull;
}
/**
* Compute the value of this lazy.
*/
protected abstract E compute();
/**
* Specify whether this lazy should garbage collect its value if needed,
* or whether it should force it to be persistent.
*/
protected abstract boolean shouldGC();
/**
* Get the value of this {@link Lazy} if it's been initialized, or else
* return null.
*/
public E getIfDefined() {
if (implOrNullCache != null) {
return implOrNullCache.get();
} else {
return implOrNull;
}
}
/**
* Create a degenerate {@link Lazy}, which simply returns the given pre-computed
* value.
*/
public static <E> Lazy<E> from(final E definedElement) {
Lazy<E> rtn = new Lazy<E>() {
@Override
protected E compute() {
return definedElement;
}
@Override
protected boolean shouldGC() {
return false;
}
};
rtn.implOrNull = definedElement;
return rtn;
}
/**
* Create a lazy value from the given provider.
* The provider is only called once on initialization.
*/
public static <E> Lazy<E> of(Supplier<E> fn) {
return new Lazy<E>() {
@Override
protected E compute() {
return fn.get();
}
@Override
protected boolean shouldGC() {
return false;
}
};
}
/**
* Create a lazy value from the given provider, allowing the value
* stored in the lazy to be garbage collected if necessary.
* The value is then re-created by when needed again.
*/
public static <E> Lazy<E> cache(Supplier<E> fn) {
return new Lazy<E>() {
@Override
protected E compute() {
return fn.get();
}
@Override
protected boolean shouldGC() {
return true;
}
};
}
}