Allow combining memoized objects
package com.yahoo.yolean.concurrent;
+import java.util.function.Function;
import java.util.function.Supplier;
import static java.util.Objects.requireNonNull;
@@ -14,9 +15,16 @@ import static java.util.Objects.requireNonNull;
*/
public class Memoized<T, E extends Exception> implements Supplier<T>, AutoCloseable {
- /** Provides a tighter bound on the thrown exception type. */
+ /**
+ * Provides a tighter bound on the thrown exception type.
+ */
@FunctionalInterface
- public interface Closer<T, E extends Exception> { void close(T t) throws E; }
+ public interface Closer<T, E extends Exception> {
+
+ void close(T t) throws E;
+
+ }
+
private final Object monitor = new Object();
private final Closer<T, E> closer;
@@ -32,6 +40,10 @@ public class Memoized<T, E extends Exception> implements Supplier<T>, AutoClosea
return new Memoized<>(factory, AutoCloseable::close);
}
+ public static <T, U, E extends Exception, F extends E, G extends E> Memoized<U, E> combine(Memoized<T, F> inner, Function<T, U> outer, Closer<U, G> closer) {
+ return new Memoized<>(() -> outer.apply(inner.get()), compose(closer, inner::close));
+ }
+
@Override
public T get() {
// Double-checked locking: try the variable, and if not initialized, try to initialize it.
@@ -61,4 +73,28 @@ public class Memoized<T, E extends Exception> implements Supplier<T>, AutoClosea
}
}
+ private interface Thrower<E extends Exception> { void call() throws E; }
+
+ private static <T, E extends Exception, F extends E, G extends E> Closer<T, E> compose(Closer<T, G> inner, Thrower<F> outer) {
+ return parent -> {
+ Exception thrown = null;
+ try {
+ inner.close(parent);
+ }
+ catch (Exception e) {
+ thrown = e;
+ }
+ try {
+ outer.call();
+ }
+ catch (Exception e) {
+ if (thrown != null) thrown.addSuppressed(e);
+ else thrown = e;
+ }
+ @SuppressWarnings("unchecked")
+ E e = (E) thrown;
+ if (e != null) throw e;
+ };
+ }
+
}
\ No newline at end of file
