For those who need the functionality mentioned in the question: I have improved it a bit in order to ideally reflect the behavior of the existing CompletableFuture#completeAsync
and CompletableFuture.supplyAsync
functions.
// Java 9+ (Java 8 version below)
import java.util.concurrent.Callable;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.Executor;
import java.util.function.Supplier;
/**
* {@link CompletableFuture} utils.
*
* @author stonar96
*
* @see CompletableFuture
*/
public final class CompletableFutureUtils {
/**
* Delegates the given Callable to
* {@link CompletableFuture#completeAsync(Supplier)} using a new
* CompletableFuture and handles checked exceptions accordingly to unchecked
* exceptions.
*
* @param callable a function returning the value to be used to complete the
* returned CompletableFuture
* @param <U> the function's return type
* @return the new CompletableFuture
* @see CompletableFuture#completeAsync(Supplier)
*/
public static <U> CompletableFuture<U> callAsync(Callable<? extends U> callable) {
return completeAsync(new CompletableFuture<>(), callable);
}
/**
* Delegates the given Callable to
* {@link CompletableFuture#completeAsync(Supplier)} using the given
* CompletableFuture and handles checked exceptions accordingly to unchecked
* exceptions.
*
* @param result the CompletableFuture to be used
* @param callable a function returning the value to be used to complete the
* returned CompletableFuture
* @param <T> the function's return type
* @return the given CompletableFuture
* @see CompletableFuture#completeAsync(Supplier)
*/
public static <T> CompletableFuture<T> completeAsync(CompletableFuture<T> result, Callable<? extends T> callable) {
return result.completeAsync(callable == null ? null : () -> {
try {
return callable.call();
} catch (Error e) {
throw e;
} catch (RuntimeException e) {
throw e; // Also avoids double wrapping CompletionExceptions below.
} catch (Throwable t) {
throw new CompletionException(t);
}
});
}
/**
* Delegates the given Callable and Executor to
* {@link CompletableFuture#completeAsync(Supplier, Executor)} using a new
* CompletableFuture and handles checked exceptions accordingly to unchecked
* exceptions.
*
* @param callable a function returning the value to be used to complete the
* returned CompletableFuture
* @param executor the executor to use for asynchronous execution
* @param <U> the function's return type
* @return the new CompletableFuture
* @see CompletableFuture#completeAsync(Supplier, Executor)
*/
public static <U> CompletableFuture<U> callAsync(Callable<? extends U> callable, Executor executor) {
return completeAsync(new CompletableFuture<>(), callable, executor);
}
/**
* Delegates the given Callable and Executor to
* {@link CompletableFuture#completeAsync(Supplier, Executor)} using the given
* CompletableFuture and handles checked exceptions accordingly to unchecked
* exceptions.
*
* @param result the CompletableFuture to be used
* @param callable a function returning the value to be used to complete the
* returned CompletableFuture
* @param executor the executor to use for asynchronous execution
* @param <T> the function's return type
* @return the given CompletableFuture
* @see CompletableFuture#completeAsync(Supplier, Executor)
*/
public static <T> CompletableFuture<T> completeAsync(CompletableFuture<T> result, Callable<? extends T> callable, Executor executor) {
return result.completeAsync(callable == null ? null : () -> {
try {
return callable.call();
} catch (Error e) {
throw e;
} catch (RuntimeException e) {
throw e; // Also avoids double wrapping CompletionExceptions below.
} catch (Throwable t) {
throw new CompletionException(t);
}
}, executor);
}
private CompletableFutureUtils() {
throw new AssertionError("CompletableFutureUtils cannot be instantiated");
}
}
// Java 8
import java.util.concurrent.Callable;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.Executor;
import java.util.function.Supplier;
/**
* {@link CompletableFuture} utils.
*
* @author stonar96
*
* @see CompletableFuture
*/
public final class CompletableFutureUtils {
/**
* Delegates the given Callable to
* {@link CompletableFuture#supplyAsync(Supplier)} and handles checked
* exceptions accordingly to unchecked exceptions.
*
* @param callable a function returning the value to be used to complete the
* returned CompletableFuture
* @param <U> the function's return type
* @return the new CompletableFuture
* @see CompletableFuture#supplyAsync(Supplier)
*/
public static <U> CompletableFuture<U> callAsync(Callable<? extends U> callable) {
return CompletableFuture.supplyAsync(callable == null ? null : () -> {
try {
return callable.call();
} catch (Error e) {
throw e;
} catch (RuntimeException e) {
throw e; // Also avoids double wrapping CompletionExceptions below.
} catch (Throwable t) {
throw new CompletionException(t);
}
});
}
/**
* Delegates the given Callable to
* {@link CompletableFuture#supplyAsync(Supplier)}, handles checked exceptions
* accordingly to unchecked exceptions and delegates to and from the given
* CompletableFuture.
*
* @param result the CompletableFuture to be delegated
* @param callable a function returning the value to be used to complete the
* returned CompletableFuture
* @param <T> the function's return type
* @return the given delegated CompletableFuture
* @see CompletableFuture#supplyAsync(Supplier)
*/
public static <T> CompletableFuture<T> completeAsync(CompletableFuture<T> result, Callable<? extends T> callable) {
if (result == null) {
throw new NullPointerException();
}
CompletableFuture<T> delegate = callAsync(callable == null ? null : () -> result.isDone() ? null /* (1) */ : callable.call());
// (1): This is very unlikely because result delegates to delegate (see below).
// If result is completed, delegate is also completed.
// Thus supplyAsync will not even call our callable.
// However, there may be a race condition where they are not already delegated.
// In this case result will be completed by delegate with null.
// This does not matter because result is already completed.
if (delegate == null) {
return null;
}
result.whenComplete((v, t) -> {
if (t == null) {
delegate.complete(v);
return;
}
delegate.completeExceptionally(t);
});
delegate.whenComplete((v, t) -> {
if (t == null) {
result.complete(v);
return;
}
result.completeExceptionally(t);
});
return result;
}
/**
* Delegates the given Callable and Executor to
* {@link CompletableFuture#supplyAsync(Supplier, Executor)} and handles checked
* exceptions accordingly to unchecked exceptions.
*
* @param callable a function returning the value to be used to complete the
* returned CompletableFuture
* @param executor the executor to use for asynchronous execution
* @param <U> the function's return type
* @return the new CompletableFuture
* @see CompletableFuture#supplyAsync(Supplier, Executor)
*/
public static <U> CompletableFuture<U> callAsync(Callable<? extends U> callable, Executor executor) {
return CompletableFuture.supplyAsync(callable == null ? null : () -> {
try {
return callable.call();
} catch (Error e) {
throw e;
} catch (RuntimeException e) {
throw e; // Also avoids double wrapping CompletionExceptions below.
} catch (Throwable t) {
throw new CompletionException(t);
}
}, executor);
}
/**
* Delegates the given Callable and Executor to
* {@link CompletableFuture#supplyAsync(Supplier, Executor)}, handles checked
* exceptions accordingly to unchecked exceptions and delegates to and from the
* given CompletableFuture.
*
* @param result the CompletableFuture to be delegated
* @param callable a function returning the value to be used to complete the
* returned CompletableFuture
* @param executor the executor to use for asynchronous execution
* @param <T> the function's return type
* @return the given delegated CompletableFuture
* @see CompletableFuture#supplyAsync(Supplier, Executor)
*/
public static <T> CompletableFuture<T> completeAsync(CompletableFuture<T> result, Callable<? extends T> callable, Executor executor) {
if (result == null) {
throw new NullPointerException();
}
CompletableFuture<T> delegate = callAsync(callable == null ? null : () -> result.isDone() ? null /* (1) */ : callable.call(), executor);
// (1): This is very unlikely because result delegates to delegate (see below).
// If result is completed, delegate is also completed.
// Thus supplyAsync will not even call our callable.
// However, there may be a race condition where they are not already delegated.
// In this case result will be completed by delegate with null.
// This does not matter because result is already completed.
if (delegate == null) {
return null;
}
result.whenComplete((v, t) -> {
if (t == null) {
delegate.complete(v);
return;
}
delegate.completeExceptionally(t);
});
delegate.whenComplete((v, t) -> {
if (t == null) {
result.complete(v);
return;
}
result.completeExceptionally(t);
});
return result;
}
private CompletableFutureUtils() {
throw new AssertionError("CompletableFutureUtils cannot be instantiated");
}
}
As you can see, everything is just delegated as is, except for checked exceptions, which must be handled.