Est-il un moyen élégant pour obtenir la première valeur non nulle de plusieurs méthode retourne en Java?
Vous l'avez déjà vu à de nombreuses reprises vous-même, de qui je suis sûr:
public SomeObject findSomeObject(Arguments args) {
SomeObject so = queryFirstSource(args); //the most likely source first, hopefully
if (so != null) return so;
so = querySecondSource(args); //a source less likely than the first, hopefully
if (so != null) return so;
so = queryThirdSource(args); //a source less likely than the previous, hopefully
if (so != null) return so;
//and so on
}
Nous avons différentes sources où un objet de recherche pourrait être. Comme un exemple frappant nous pourrions image que nous avons d'abord vérifier si un utilisateur est dans la liste des utilisateurs privilégiés. Si pas, nous vérifions si le nom est dans la liste des utilisateurs autorisés. D'autre nous retourner la valeur null. (C'est pas le meilleur exemple, mais j'espère que c'est un vif-assez.)
Goyave nous offre quelques aides que l'on pourrait embellir le code ci-dessus:
public SomeObject findSomeObject(Arguments args) {
//if there are only two objects
return com.google.common.base.Objects.firstNonNull(queryFirstSource(args), querySecondSource(args));
//or else
return com.google.common.collect.Iterables.find(
Arrays.asList(
queryFirstSource(args)
, querySecondSource(args)
, queryThirdSource(args)
//, ...
)
, com.google.common.base.Predicates.notNull()
);
}
Mais, comme les plus expérimentés d'entre nous ont déjà vu, cela peut mal fonctionner si les recherches (c'est à dire queryXXXXSource(args)
) prendre un certain temps. C'est parce que nous avons maintenant la requête de toutes les sources d'abord, puis de transmettre les résultats à la méthode qui trouve le premier parmi ces résultats, qui n'est pas null
.
Contrairement au premier exemple, où la source suivante n'est évaluée que lorsque le premier n'a pas de retour de quelque chose, cette deuxième solution peut regarder mieux au début, mais pourrait faire bien pire.
Voici où nous en venir à ma question et à qui je suggère quelque chose, j'espère que quelqu'un a déjà mis en place la base de la ou des que quelqu'un pourrait proposer un même smarted solution.
En anglais: quelqu'un A déjà mis en place une telle defferedFirstNonNull
(voir ci-dessous) ou quelque chose de similaire? Est-il facile de la plaine-Java solution à réaliser avec les nouvelles Flux cadre? Pouvez-vous proposer une autre solution élégante qui permet d'obtenir le même résultat?
Règles: Java 8 est autorisé ainsi que active entretenus et bien connu des bibliothèques tierces comme Google Goyave ou Apache Commons Lang avec la Licence Apache ou similaires (Pas de GPL!).
La solution proposée:
public SomeObject findSomeObject(Arguments args) {
return Helper.deferredFirstNonNull(
Arrays.asList(
args -> queryFirstSource(args)
, args -> querySourceSource(args)
, args -> queryThirdSource(args)
)
, x -> x != null
)
}
Donc la méthode defferedFirstNonNull
d'évaluer chaque expression lambda après l'autre, et dès que le prédicat (x -> x != null
) est vrai (c'est à dire, nous avons trouvé une correspondance), la méthode retourne le résultat immédiatement, et ne serait pas de requête de tout autre source.
PS: je sais que les expressions args -> queryXXXXSource(args)
pourrait être réduit à queryXXXXSource
. Mais cela rendrait la solution proposée plus difficile à lire parce qu'il n'est pas évident, à première vue, qu'est-ce que va se passer.
OriginalL'auteur cimnine | 2014-09-30
Vous devez vous connecter pour publier un commentaire.
Elle dépend de certains facteurs que vous n'êtes pas définir. Avez-vous un fixe, plutôt petit ensemble de
query…Source
actions comme indiqué dans votre question ou êtes-vous plutôt-tête pour avoir une plus souple, extensible liste d'actions?Dans le premier cas, vous pouvez envisager de modifier la
query…Source
méthodes de retour d'unOptional<SomeObject>
plutôt queSomeObject
ounull
. Si vous changez vos méthodes pour être commeVous pouvez la chaîne de cette façon:
Si vous ne pouvez pas les modifier ou les préfèrent au retour
null
vous pouvez toujours utiliser leOptional
classe:Si vous cherchez un moyen plus souple pour un plus grand nombre de requêtes, il est inévitable de les convertir en une sorte de liste ou de flux de
Function
s. Une solution possible est:Effectue l'opération souhaitée, toutefois, il sera nécessaire de composer l'action à chaque fois que vous appelez la méthode. Si vous voulez vous prévaloir de cette méthode le plus souvent, vous pouvez envisager de composer une opération que vous pouvez ré-utiliser:
Donc, vous voyez, il y a plus d'une façon. Et cela dépend de la tâche dans quelle direction aller. Parfois, le simple fait de
if
séquence peut être approprié.Optional<>
classe et je pense qu'il fournit la manière la plus élégante pour mon problème.OriginalL'auteur Holger
Oui, il y a:
C'est plus souple, puisqu'elle retourne un
Optional
pasnull
en cas de non-nullsource
est trouvé.Edit: Si vous voulez paresseux évaluation, vous devez utiliser un
Supplier
:Stream
de varargs, vous pouvez tout aussi bien appelerStream.of(source1, source2, ...)
au lieu deArrays.asList(source1, source2, ...).stream()
OriginalL'auteur m3th0dman
Je voudrais écrire comme ceci (vous ne pouvez pas besoin de génériques ici, mais pourquoi ne pas le faire):
Et vous pouvez l'appeler avec:
(en supposant que votre
queryXXX
méthodes sont des méthodes d'instance)Les méthodes seront appliquées dans l'ordre jusqu'à ce que l'on retourne une valeur qui correspond au prédicat (dans l'exemple ci-dessus: renvoie une valeur non nulle).
OriginalL'auteur assylias