99 lines
2.5 KiB
Java
99 lines
2.5 KiB
Java
package tc.oc.commons.core.util;
|
|
|
|
import java.time.Duration;
|
|
import java.time.Instant;
|
|
import java.util.function.Supplier;
|
|
import javax.inject.Inject;
|
|
import javax.inject.Provider;
|
|
|
|
/**
|
|
* General-purpose container for a lazily calculated value.
|
|
* Both thread-safe and thread-unsafe variants are available.
|
|
*
|
|
* Lazy<String> text = Lazy.from(() -> "Expensive string");
|
|
* text.get();
|
|
*
|
|
* There is also an injectable constructor that injects a Provider<T>,
|
|
* so you can simply self-bind this class to get a cached provider
|
|
* of anything.
|
|
*/
|
|
public class Lazy<T> implements Supplier<T>, Provider<T> {
|
|
|
|
private final Supplier<T> supplier;
|
|
private boolean got;
|
|
private T instance;
|
|
|
|
@Inject private Lazy(Provider<T> provider) {
|
|
this.supplier = provider::get;
|
|
}
|
|
|
|
private Lazy(Supplier<T> supplier) {
|
|
this.supplier = supplier;
|
|
}
|
|
|
|
@Override
|
|
public T get() {
|
|
if(!got) {
|
|
instance = supplier.get();
|
|
got = true;
|
|
}
|
|
return instance;
|
|
}
|
|
|
|
public void clear() {
|
|
got = false;
|
|
instance = null;
|
|
}
|
|
|
|
/**
|
|
* Create a lazy value calculated by the given {@link Supplier}.
|
|
*
|
|
* The lazy container is not thread-safe. If the value is
|
|
* retrieved concurrently from multiple threads, it may be
|
|
* calculated more than once.
|
|
*/
|
|
public static <T> Lazy<T> from(Supplier<T> supplier) {
|
|
return new Lazy<>(supplier);
|
|
}
|
|
|
|
/**
|
|
* Create a thread-safe lazy value calculated by the given {@link Supplier}.
|
|
*/
|
|
public static <T> Lazy<T> sync(Supplier<T> supplier) {
|
|
return new SyncLazy<>(supplier);
|
|
}
|
|
|
|
private static class SyncLazy<T> extends Lazy<T> {
|
|
private SyncLazy(Supplier<T> supplier) {
|
|
super(supplier);
|
|
}
|
|
|
|
@Override
|
|
synchronized public T get() {
|
|
return super.get();
|
|
}
|
|
}
|
|
|
|
public static <T> Lazy<T> expiring(Duration maxAge, Supplier<T> supplier) {
|
|
return new Expiring<T>(maxAge, supplier);
|
|
}
|
|
|
|
private static class Expiring<T> extends Lazy<T> {
|
|
private final Duration max;
|
|
private Instant last = TimeUtils.INF_PAST;
|
|
|
|
private Expiring(Duration max, Supplier<T> supplier) {
|
|
super(supplier);
|
|
this.max = max;
|
|
}
|
|
|
|
@Override
|
|
public T get() {
|
|
if(Comparables.greaterThan(Duration.between(last, Instant.now()), max)) {
|
|
clear();
|
|
}
|
|
return super.get();
|
|
}
|
|
}
|
|
}
|