Facultatif vs lancer une exception

Est-il vrai que, depuis que Java 1.8 renvoie un object Optional , est préférable à une exception? De plus en plus, je vois le code comme ceci:

  public Optional get(int i) { // do somtething Object result = ... Optional.ofNullable(result); } 

Au lieu de cela:

 public Object get(int i) { if(i=size) { throw new IndexOutOfBoundsException("Index: " + i + ". Size: " + size); } // do somtething Object result = ... return result; } 

Est-ce que cela signifie que nous devons oublier l’ancienne approche et utiliser de nouvelles? Et où Optional est approprié du tout?

L’exemple que vous présentez ne constitue pas une utilisation appropriée de Optional. Une option facultative vide représente une valeur absente pour une raison qui n’a pas pu être prédite par l’appelant. C’est le résultat d’une invocation légale d’une méthode.

Le code que vous présentez en tant qu ‘”ancien idiome” effectue la validation de l’entrée et lève une exception non contrôlée si l’entrée n’était pas valide. Ce comportement devrait restr inchangé même si vous introduisez Facultatif. La seule différence serait que la valeur Object get(i) par Object get(i) est peut-être nulle alors que la valeur Optional get(i) par Optional get(i) n’est jamais nulle, car il existe un état spécial de l’instance Optional qui représente l’absence de valeur. .

L’avantage des méthodes qui renvoient Facultatif au lieu d’une valeur nullable est l’élimination du code standard qui doit effectuer une vérification de routine à zéro avant de tenter de faire quoi que ce soit avec la valeur renvoyée. Il y a beaucoup plus d’avantages à utiliser Optional uniquement dans une méthode. Par exemple:

 static Optional componentType(Type type) { return Optional.of(type) .filter(t -> t instanceof ParameterizedType) .map(t -> (ParameterizedType) t) .filter(t -> t.getActualTypeArguments().length == 1) .filter(t -> Optional.of(t.getRawType()) .filter(rt -> rt instanceof Class) .map(rt -> (Class) rt) .filter(Stream.class::isAssignableFrom) .isPresent()) .map(t -> t.getActualTypeArguments()[0]); 

Ici, un avantage important est le contrôle parfait de la scope: le même nom t est réutilisé dans chaque nouvelle scope pour une variable d’un type approprié à cette étape du traitement. Ainsi, au lieu d’être obligés d’avoir des variables dans la scope une fois leur durée de vie utile écasting et d’inventer un nouveau nom pour chaque variable suivante, nous avons avec cet idiome le minimum précis dont nous avons besoin pour procéder.

Juste pour votre intérêt, vous pouvez implémenter equals entièrement en termes de facultatif:

 @Override public boolean equals(Object obj) { return Optional.ofNullable(obj) .filter(that -> that instanceof Test) .map(that -> (Test)that) .filter(that -> Objects.equals(this.s1, that.s1)) .filter(that -> Objects.equals(this.s2, that.s2)) .isPresent(); } 

Bien que je trouve cet idiome très propre et lisible, il n’est pas encore suffisamment optimisé pour être recommandé comme choix de production. Les futures versions de Java pourraient rendre cela viable, cependant.

Il est possible d’abuser des exceptions, des nulls et des options sur un pied d’égalité. Dans ce cas particulier, je pense que vous abusez probablement de l’option, car vous cachez en silence une violation préalable et vous la convertissez en une déclaration normale. À la réception d’une option vide de votre code, l’appelant n’a aucun moyen de différencier “la chose que je cherchais ne s’y trouvait pas” et “j’ai posé une question invalide”.

Comme Facultatif est nouveau, il a également tendance à être surexploité. Espérons qu’avec le temps, les bons modèles seront intériorisés.

Facultatif est un exemple du modèle d’object null ; c’est un moyen sûr de dire «rien n’était là» quand «rien là-bas» ne donne un résultat attendu raisonnable. (Le renvoi d’un tableau vide ou d’une collection vide sont des exemples similaires dans ces domaines.) Si vous souhaitez représenter “rien là-bas” par null / facultatif par rapport à une exception dépend généralement du fait que “rien là-bas” est une situation couramment attendue, ou si c’est exceptionnel. Par exemple, personne ne veut que Map.get une exception si le mappage n’est pas présent; le mapping-not-present est un résultat attendu, pas exceptionnel. (Si nous avions Optional en 1997, Map.get aurait renvoyé un Optional .)

Je ne sais pas où vous avez entendu le conseil selon lequel Optional est préférable aux exceptions, mais celui qui vous a dit cela était mal informé. Si vous avez déjà lancé une exception, vous devriez probablement toujours lancer une exception. Si vous avez renvoyé null avant, vous pouvez envisager de retourner Optional place.

Dans les cas où des erreurs pourraient survenir, le type de données approprié est Try.

Au lieu d’utiliser les abstractions “présent” ou “vide”, un Try utilise les abstractions “failure” ou “success”.

Comme Try n’est pas fourni par Java 8, il est nécessaire d’utiliser une 3. bibliothèque de parties. (Peut-être que nous le verrons ajouté à Java 9?)

Essayer pour Java