Je suis en train de mettre en place un système simple promesse en java. Je le fais pour usage spécial afin s'il vous plaît ne recommande pas de bibliothèques.
J'ai un problème lorsque je tente de mettre en œuvre une thenApply()
méthode qui prend une fonction en tant que paramètre, semblable à ce que CompletableFuture a et renvoie donc une promesse avec un autre type.
L'interface de promesse:
public interface Promise<T> {
Promise<T> then(Consumer<T> handler);
<U> Promise<U> thenApply(Function<T, U> handler);
}
Ma mise en œuvre à ce jour:
public class PromiseImpl<T> implements Promise<T> {
private List<Consumer<T>> resultHandlers = new ArrayList<>();
public PromiseImpl(CompletableFuture<T> future) {
future.thenAccept(this::doWork);
}
@Override
public Promise<T> then(Consumer<T> handler) {
resultHandlers.add(handler);
return this;
}
@Override
public <U> Promise<U> thenApply(Function<T, U> handler) {
// How to implement here??? I don't have the result yet
handler.apply(?);
}
private void onResult(T result) {
for (Consumer<T> handler : resultHandlers) {
handler.accept(result);
}
}
private Object doWork(T result) {
onResult(result);
return null;
}
}
Le problème est que je ne sais pas le résultat de mon avenir initial dans la thenApply()
méthode, donc je ne peux pas appeler mon gestionnaire. Je ne veux pas aussi appeler future.get()
parce que cette méthode bloque.
Comment pourrais-je faire ce travail?
Le vrai problème est dans la conception de votre Promise
genre. Il tient un ensemble de callbacks, tous doivent être invoquées à la fin. Ceci est un problème fondamental (limitation de fonctionnalité générique autour du type de retour de thenApply
la fonction). Cela peut être résolu en changeant votre Promise
mise en œuvre de retourner une new
promesse à chaque fois qu'un gestionnaire est enregistré, au lieu de retourner this
, de sorte que chaque objet promesse aura son propre gestionnaire d'invoquer.
En plus de résoudre cela, il est une meilleure conception pour la programmation de style fonctionnel, que vous pouvez faire vos Promise
objets immuables.
Je change l'interface être:
interface Promise<T> {
<U> Promise<U> thenApply(Function<T, U> handler);
Promise<Void> thenAccept(Consumer<T> consumer);
}
Le « Enchaînement » de callbacks peut alors se faire autour des objets futurs auxquels chaînés Promise
instances ont des références. Ainsi , la mise en œuvre peut ressembler à :
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;
}
}
Et à l'aide qui peut être aussi simple que:
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:
En ce qui concerne le commentaire de Michael sur la récupération de la valeur: qui n'a pas été ajouté comme il n'a pas été dans l'original Promise
API. Mais il est assez facile d'ajouter:
T get(); //To the interface
Et mis en œuvre avec:
public T get() {
//try-catch
return this.future.get();
}
Note: cela commence à ressembler de plus en plus comme une duplication de CompletableFuture
, ce qui pose la question de savoir pourquoi faire du tout. Mais en supposant qu'il y aura d' autres Promise
méthodes -comme dans cette interface, la méthode serait envelopper l'API future.
Si vous avez besoin d'utiliser le même Promise
objet avec une liste de dossiers d'appel, alors vous avez pas d'autre choix que de paramétrer l' Promise
interface avec les deux Function
paramètres de type béton:
public interface Promise<T, U>
Et U
ne serait pas en mesure d'être une méthode paramètre générique sur then
ou thenApply
.