Настоящая проблема заключается в дизайне вашего типа Promise
. Он содержит набор обратных вызовов, которые должны быть вызваны по завершении. Это фундаментальная проблема (ограничение универсальной функциональности вокруг типа, возвращаемого функцией thenApply
). Эту проблему можно решить, изменив реализацию Promise
так, чтобы она возвращала обещание new
всякий раз, когда обработчик зарегистрирован, вместо возврата this
, так что каждый объект обещания будет иметь свой собственный обработчик для вызова.
В дополнение к решению этой проблемы, это лучший дизайн для программирования в функциональном стиле, поскольку вы можете сделать ваши Promise
объекты неизменяемыми.
Я бы изменил интерфейс на:
interface Promise<T> {
<U> Promise<U> thenApply(Function<T, U> handler);
Promise<Void> thenAccept(Consumer<T> consumer);
}
Затем можно создать «цепочку» обратных вызовов вокруг будущих объектов, на которые ссылаются цепочки Promise
. Так что реализация может выглядеть так:
class PromiseImpl<T> implements Promise<T> {
private CompletableFuture<T> future;
public PromiseImpl(CompletableFuture<T> future) {
this.future = future;
}
@Override
public <U> Promise<U> thenApply(Function<T, U> function) {
return new PromiseImpl<>(this.future.thenApply(function));
}
@Override
public Promise<Void> thenAccept(Consumer<T> consumer) {
return new PromiseImpl<>(this.future.thenAccept(consumer));
}
private void onResult(T result) {
this.future.complete(result);
}
private Object doWork(T result) {
onResult(result);
return null;
}
}
И использовать это можно так же просто, как:
Promise<String> stringPromise = new PromiseImpl<>(new CompletableFuture<String>());
Promise<Long> longPromise = stringPromise.thenApply(str -> Long.valueOf(str.length()));
Promise<Void> voidPromise = stringPromise.thenAccept(str -> System.out.println(str));
EDIT:
Относительно комментария Майкла о получении значения: оно не было добавлено, как не было в оригинальном Promise
API. Но достаточно просто добавить:
T get(); //To the interface
И реализовано с помощью:
public T get() {
//try-catch
return this.future.get();
}
Примечание: это начинает все больше и больше походить на дублирование CompletableFuture
, что поднимает вопрос, зачем вообще это делать. Но при условии, что в этом интерфейсе появятся дополнительные Promise
-подобные методы, метод обернет будущий API.
Если вам нужно использовать один и тот же объект Promise
со списком обратных вызовов, у вас нет другого выбора, кроме как параметризовать интерфейс Promise
с обоими Function
параметрами конкретного типа:
public interface Promise<T, U>
И U
не сможет быть универсальным параметром метода для then
или thenApply
.