web-dev-qa-db-fra.com

Renvoyer CompletableFuture <vide> ou CompletableFuture <?>?

Je veux écrire une méthode asynchrone qui retourne un CompletableFuture . Le seul objectif du futur est de savoir quand la méthode est terminée et non son résultat. Serait-il préférable de retourner CompletableFuture<Void> Ou CompletableFuture<?>? Existe-t-il une raison de préférer l'une ou l'autre ou sont-ils interchangeables?

Notez que je ne pose que des questions sur les types de retour, pas sur les listes de paramètres, les déclarations de variables ou d'autres contextes.

44
John Kugelman

Il vaut mieux utiliser CompletableFuture<Void>.

Selon cette réponse trouvé par Sotirios Delimanolis , Future<?> Est une faille mineure de l'API. Dans Java 6, la méthode submit() utilisait un Future<Object> En interne et son type de retour était donc défini sur Future<?>. In Java 7 l'implémentation a été modifiée pour utiliser Future<Void> En interne, mais il était trop tard pour modifier l'API et la valeur de retour est restée la même que Future<?>.

Les plus récentes Java utilisent Future<Void> Et CompletableFuture<Void>. Ce sont les exemples à suivre.

19
John Kugelman

Serait-il préférable de retourner CompletableFuture <vide> ou CompletableFuture <?>

Existe-t-il une raison de préférer l'une ou l'autre ou sont-ils interchangeables?

Le code peut affecter trois contextes:

  • Runtime - les génériques n’ont aucune implication.
  • Compile - Je ne peux pas imaginer le cas où une méthode accepterait Future<Void> mais n'accepte pas Future<?>.
  • Développement - Si le résultat de Future n'a pas de sens, il est recommandé de le signaler aux utilisateurs via la déclaration.

Alors Future<Void> est plus préférable.

17
user3707125

En regardant l'API CompletableFuture, vous constaterez que CompletableFuture<Void> est utilisé avec des effets secondaires comme des méthodes où le résultat ne peut pas être obtenu (parce qu’il n’existe pas), ex:

CompletableFuture.runAsync(Runnable runnable);

renvoyer un CompletableFuture<Object> _ ici serait déroutant car il n’ya aucun résultat vraiment, nous ne nous soucions que de l’achèvement Les méthodes qui prennent Consumers et Runnables retournent CompletableFuture<Void>, ex: thenAccept, thenAcceptAsync. Consumer et Runnable sont utilisés pour les effets secondaires en général.

Un autre cas d'utilisation de Void est lorsque vous ne connaissez vraiment pas le résultat. Par exemple: CompletableFuture.allOf, la liste transmise peut être un CompletableFuture provenant d’un Runnable, nous ne pouvons donc pas obtenir le résultat.

Ceci dit, CompletableFuture<Void> n’est bon que si vous n’avez pas d’autre option, si vous pouvez renvoyer le résultat, merci de le faire, l’appelant peut choisir de le rejeter s’il n’est pas intéressé. Vous avez dit que vous êtes seulement intéressé par l'achèvement, alors oui, CompletableFuture<Void> ferait le travail, mais les utilisateurs de votre API vous détesteraient s’ils savaient que CompletableFuture<T> était une option et vous venez de décider en leur nom qu’ils n’auront jamais besoin du résultat.

9
Sleiman Jneidi

Le type approprié dépend de sa sémantique. Toutes les options répertoriées promettent de signaler l’achèvement et peuvent renvoyer des exceptions de manière asynchrone.

  • CompletableFuture<Void>: Le Void indique à l'utilisateur qu'aucun résultat n'est attendu.
  • CompletableFuture<?> Le ? Signifie que le type de la valeur contient est indéfini dans le sens où toute valeur pourrait être livrée.

La classe CompletableFuture hérite de plusieurs méthodes pratiques de CompletionStage. Mais cela permet également à l'appelant de votre méthode de déclencher l'achèvement de l'avenir, ce qui semble faux, car votre méthode est responsable de signaler son achèvement elle-même. Il existe également une méthode cancel(...) qui est plutôt inutile dans l'implémentation par défaut de CompletableFuture car elle n'annule pas l'exécution.

  • Future<Void>: Le Void indique à l'utilisateur qu'aucun résultat n'est attendu.
  • Future<?> Le ? Signifie que le type de la valeur contient est indéfini dans le sens où toute valeur pourrait être livrée.

Future ne dispose pas des méthodes pratiques de CompletionStage. Il ne permet pas de déclencher l'achèvement du futur mais l'exécution peut être annulée.

L'option suivante est CompletionStage<Void>:

  • CompletionStage<Void>: Le Void indique à l'utilisateur qu'aucun résultat n'est attendu. Les méthodes pratiques pour lier les gestionnaires sont présentes, mais pas la méthode cancel(...). L'appelant de votre méthode ne peut pas déclencher l'achèvement d'un CompletionStage.
  • <CancellableFuture extends Future<Void> & CompletionStage<Void>>: Ensemble de méthodes de Future<Void> Et CompletionStage<Void>. Il indique qu'il n'y a pas de résultat, que des méthodes pratiques sont présentes ainsi que la possibilité d'annuler. L'appelant de votre méthode ne peut pas déclencher l'achèvement d'un CompletionStage.

L'absence de la méthode cancel(...) peut convenir à votre scénario ou non. C'est pourquoi je suggère d'utiliser CompletionStage<Void> Si vous n'avez pas besoin d'annulation et d'utiliser <CancellableFuture extends Future<Void> & CompletionStage<Void>> Si vous avez besoin de l'option pour annuler l'exécution. Si vous avez choisi <CancellableFuture extends Future<Void> & CompletionStage<Void>>, Vous voudrez probablement créer vous-même une interface héritant de Future<Void> Et de CompletionStage<Void> À utiliser comme type de retour au lieu de placer l'intersection de type long directement dans votre méthode. déclaration.

Vous devriez éviter de renvoyer avec un type de retour déclaré CompletableFuture en raison de la possibilité pour l'appelant de déclencher l'achèvement de l'avenir. Cela conduit délibérément à du code déroutant et à des problèmes surprenants, car il est difficile de savoir quel code est responsable de l'activation. Utilisez l'un des types les plus restreints mentionnés pour permettre au système de types d'empêcher le déclenchement non intentionnel de l'achèvement par l'appelant de votre méthode.

5
Augustus Kling