Skip to content

Commit

Permalink
util: Add Lazy
Browse files Browse the repository at this point in the history
  • Loading branch information
kohlschuetter committed Jan 9, 2024
1 parent c2dcaf2 commit 0631e7c
Showing 1 changed file with 70 additions and 0 deletions.
70 changes: 70 additions & 0 deletions kohlschutter-util/src/main/java/com/kohlschutter/util/Lazy.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package com.kohlschutter.util;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Supplier;

/**
* Wrapper for a lazy-initialized object.
*
* @param <V> The object type.
* @author Christian Kohlschütter
*/
public final class Lazy<V> {
private final CompletableFuture<V> future = new CompletableFuture<V>();
private final AtomicBoolean supplied = new AtomicBoolean();
private final Supplier<V> supplier;

/**
* Creates a lazy-load wrapper, using the given supplier to supply the object upon the first call
* to {@link #get()}.
*
* @param <V> The object type.
* @param supplier The object supplier.
* @return The wrapper instance.
*/
public static final <V> Lazy<V> of(Supplier<V> supplier) {
return new Lazy<>(supplier);
}

private Lazy(Supplier<V> supplier) {
this.supplier = supplier;
}

/**
* 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.
*
* @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 RuntimeException(e);
}
}

/**
* 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.
*
* @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);
}

@SuppressWarnings("null")
@Override
public String toString() {
return super.toString() + "[supplied=" + supplied + "; value=" + future.getNow(null) + "]";
}
}

0 comments on commit 0631e7c

Please sign in to comment.