From a8d7b9877f5ebbce798af95a124f9a10784f8eb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Kohlschu=CC=88tter?= Date: Fri, 12 Jan 2024 17:23:39 +0100 Subject: [PATCH] util: Improve Lazy --- .../main/java/com/kohlschutter/util/Lazy.java | 103 +++++++++++++----- 1 file changed, 76 insertions(+), 27 deletions(-) diff --git a/kohlschutter-util/src/main/java/com/kohlschutter/util/Lazy.java b/kohlschutter-util/src/main/java/com/kohlschutter/util/Lazy.java index 431399f..364085a 100644 --- a/kohlschutter-util/src/main/java/com/kohlschutter/util/Lazy.java +++ b/kohlschutter-util/src/main/java/com/kohlschutter/util/Lazy.java @@ -20,6 +20,7 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Consumer; import java.util.function.Supplier; import com.kohlschutter.annotations.compiletime.SuppressFBWarnings; @@ -31,13 +32,59 @@ * @author Christian Kohlschütter */ @SuppressWarnings("PMD.ShortClassName") -public final class Lazy { - private final CompletableFuture future = new CompletableFuture(); - private final AtomicBoolean supplied = new AtomicBoolean(); - private final Supplier supplier; +public interface Lazy extends Supplier, Consumer { + static final class FromSupplier implements Lazy { + private final CompletableFuture future = new CompletableFuture(); + private final AtomicBoolean supplied = new AtomicBoolean(); + private final Supplier supplier; - private Lazy(Supplier supplier) { - this.supplier = supplier; + protected FromSupplier(Supplier supplier) { + this.supplier = supplier; + } + + @Override + public V get() { + if (!future.isDone() && supplied.compareAndSet(false, true)) { + future.complete(supplier.get()); + } + try { + return future.get(); + } catch (InterruptedException | ExecutionException e) { + throw new IllegalStateException(e); + } + } + + @Override + public boolean complete(V value) { + supplied.set(true); // NOTE: We do not compareAndSet here to allow #set from within + // supplier.get + return future.complete(value); + } + + @SuppressWarnings("null") + @Override + @SuppressFBWarnings("NP_NONNULL_PARAM_VIOLATION") + public String toString() { + return super.toString() + "[supplied=" + supplied + "; value=" + future.getNow(null) + "]"; + } + } + + static final class WithSupplied implements Lazy { + private final V supplied; + + private WithSupplied(V supplied) { + this.supplied = supplied; + } + + @Override + public V get() { + return supplied; + } + + @Override + public boolean complete(V value) { + return false; + } } /** @@ -50,43 +97,45 @@ private Lazy(Supplier supplier) { */ @SuppressWarnings("PMD.ShortMethodName") public static Lazy of(Supplier supplier) { - return new Lazy<>(supplier); + return new FromSupplier<>(supplier); + } + + /** + * Creates a {@link Lazy} wrapper, using the given value, which is regarded as instantly supplied. + * + * @param The object type. + * @param supplied The supplied object. + * @return The wrapper instance. + */ + public static Lazy ofSupplied(V supplied) { + return new WithSupplied<>(supplied); } /** * Returns the object. If this is the first call, the object is retrieved from the configured - * supplier, and transitions this instance to a completed satate. + * supplier, and this instance transitions to a completed state. * * @return The object. */ - public V get() { - if (!future.isDone() && supplied.compareAndSet(false, true)) { - future.complete(supplier.get()); - } - try { - return future.get(); - } catch (InterruptedException | ExecutionException e) { - throw new IllegalStateException(e); - } - } + @Override + V get(); /** * If not already completed, sets the value returned by {@link #get()} and related methods to the - * given value, side-stepping the value that would be retrieved from the supplier. + * given value, side-stepping the value that would be retrieved from the supplier. If already + * completed, nothing is changed and {@code false} is returned. * * @param value the result value * @return {@code true} if this invocation caused this instance to transition to a completed * state, else {@code false} */ - public boolean complete(V value) { - supplied.set(true); // NOTE: We do not compareAndSet here to allow #set from within supplier.get - return future.complete(value); - } + boolean complete(V value); - @SuppressWarnings("null") + /** + * Calls {@link #complete(Object)}, disregarding the return value. + */ @Override - @SuppressFBWarnings("NP_NONNULL_PARAM_VIOLATION") - public String toString() { - return super.toString() + "[supplied=" + supplied + "; value=" + future.getNow(null) + "]"; + default void accept(V value) { + complete(value); } }