web-dev-qa-db-fra.com

Où est le consommateur Java 8 avec plus d'un argument?

Comme dans .Net, qui fournit plusieurs implémentations du délégué Action (équivalent à Java Consumer interface fonctionnelle) avec un nombre et un type d'arguments différents, je s'attendait à ce que Java 8 fournit un moyen de spécifier un Consumer avec plus d'un argument de types différents.

Je sais que dans Java nous ne pouvons pas définir différents types avec le même nom qui diffèrent simplement dans les paramètres de type génériques, mais il y aurait de belles alternatives fluides pour fournir un multi-argument Consumer.

Existe-t-il un moyen facile de le faire, qui ne nécessite pas de définir une nouvelle interface fonctionnelle?

35
Miguel Gamboa

Pour 3 arguments et plus, vous pouvez utiliser les fonctions curried ( http://en.wikipedia.org/wiki/Currying ) avec le dernier consommateur:

Function<Double, Function<Integer, Consumer<String>>> f = d -> i -> s -> {
            System.out.println("" + d+ ";" + i+ ";" + s); 
        };
f.apply(1.0).apply(2).accept("s");

La sortie est:

1.0;2;s

Il suffit d'avoir une fonction d'un argument pour exprimer la fonction d'un nombre quelconque d'arguments: https://en.wikipedia.org/wiki/Currying#Lambda_calculi

39
Lev

Par défaut, vous êtes limité avec seulement Java.util.function.Consumer et Java.util.function.BiConsumer. Ils sont suffisants pour l'actuelle Java API de streaming. Mais vous pouvez créer vos propres interfaces fonctionnelles qui recevront autant d'arguments que vous le souhaitez et les utiliser dans vos propres API personnalisées.

18
SimY4

Il est assez facile de définir vous-même les interfaces fonctionnelles. Dans le projet sur lequel je travaille actuellement, j'ai trouvé plusieurs fois où passer une méthode en argument m'a permis de garder mon code Nice et DRY. Voici quelques interfaces fonctionnelles que j'ai définies:

@FunctionalInterface
public interface CheckedVoidFunction {
    void apply() throws Exception;
}

@FunctionalInterface
public interface VoidFunction {
    void apply();
}

@FunctionalInterface
public interface SetCategoryMethod {
    void accept(ObservableList<IRegionWithText> list, Document document, String pattern);
}

Voici une méthode qui accepte ces types d'arguments:

private void loadAnnotations(String annotationType, VoidFunction afterLoadFunction, List<Path> documentPaths) {

Et voici où j'appelle ces méthodes avec différentes méthodes comme arguments:

loadAnnotations(DocumentReader.REDACTION_ANNOTATION_TYPE, this::afterLoadRedactions, documentPaths);
loadAnnotations(DocumentReader.HIGHLIGHT_ANNOTATION_TYPE, this::afterLoadHighlights, documentPaths);

Il y a peut-être de meilleures façons de le faire, mais je pensais partager ce qui m'a été utile.

5
Mark Lackey

Existe-t-il un moyen facile de le faire, qui ne nécessite pas de définir une nouvelle interface fonctionnelle?

Un objet peut contenir n'importe quel nombre d'autres objets. L'API Streams est conçue pour diffuser un seul objet à la fois, et si vous en avez besoin de plus, vous les envelopperez dans un objet qui les contient tous.

par exemple.

Map<String, Long> map = new HashMap<>();
// each entry has a key and a value as Map.Entry<String, Long>
Map<Long, String> revMap = map.entrySet().stream()
            .collect(groupingBy(Entry::getValue(), HashMap::new, Entry::getKey));
3
Peter Lawrey