Explication du fournisseur et du consommateur Java 8 pour le profane

101

En tant que programmeur non-Java apprenant Java, je lis actuellement sur les interfaces Supplieret les Consumerinterfaces. Et je ne peux pas comprendre leur utilisation et leur signification. Quand et pourquoi utiliseriez-vous ces interfaces? Quelqu'un peut-il me donner un simple exemple profane de ceci… Je trouve que les exemples de Doc ne sont pas assez succincts pour ma compréhension.

James Emanon
la source
4
Chaque page de l'API Doc a un lien intitulé «UTILISER» en haut sur lequel vous pouvez cliquer Consumeret Suppliervous pouvez également rechercher le tutoriel pour Consumer
Holger
7
J'adore la réponse de Stuart Marks. Et je pense que la plupart des gens qui ont répondu ci-dessous ont manqué le point. La question n'est pas «comment» écrire les fournisseurs, les consommateurs et les fonctions. C'est "pourquoi" dans le monde voudriez-vous? Pour une personne qui n'y est pas habituée, ils rendent le code beaucoup plus complexe. Mais l'avantage de les utiliser n'est pas clair.
anton1980
Pour autant que je puisse voir (et je partage votre frustration avec les descriptions tangentielles), c'est juste une manière astucieuse d'abstraire à la fois le type d'objet et le traitement d'objet d'un objet utilisé dans un morceau de code. Cela permet d'appliquer ce même code à de nombreux types d'objets différents en définissant simplement différentes nouvelles classes et en les injectant dans les interfaces Fournisseur et Consommateur. Ainsi, dans un système de casier judiciaire, le même code superficiel est utilisé pour tous les suspects, mais l'impression finale pour chacun dépend de la classification de chaque suspect, par exemple `` citoyen '', `` petit '', `` larcen '', `` criminel '', `` endurci '', etc.
Tronc

Réponses:

95

C'est le fournisseur:

public Integer getInteger() {
    return new Random().nextInt();
}

C'est le consommateur:

public void sum(Integer a, Integer b) {
    System.out.println(a + b);
}

Donc, en termes simples, un fournisseur est une méthode qui renvoie une valeur (comme dans sa valeur de retour). Tandis que, un consommateur est une méthode qui consomme une certaine valeur (comme dans l'argument méthode), et effectue certaines opérations sur eux.

Ceux-ci se transformeront en quelque chose comme ceci:

// new operator itself is a supplier, of the reference to the newly created object
Supplier<List<String>> listSupplier = ArrayList::new;
Consumer<String> printConsumer = a1 -> System.out.println(a1);
BiConsumer<Integer, Integer> sumConsumer = (a1, a2) -> System.out.println(a1 + a2);

En ce qui concerne l'utilisation, l'exemple très basique serait: Stream#forEach(Consumer)method. Il faut un consommateur, qui consomme l'élément du flux sur lequel vous effectuez une itération, et effectue une action sur chacun d'eux. Imprimez-les probablement.

Consumer<String> stringConsumer = (s) -> System.out.println(s.length());
Arrays.asList("ab", "abc", "a", "abcd").stream().forEach(stringConsumer);
Rohit Jain
la source
3
Ainsi, un fournisseur est un moyen de créer une instance d'une méthode qui renvoie «quelque chose»?
james emanon
3
@jamesemanon Exactement. Cela peut être une référence de méthode ou un lambda.
Rohit Jain
14
Quel est l'avantage de cela plutôt que d'appeler la méthode directement? Est-ce parce que le fournisseur peut agir comme un intermédiaire et transmettre cette valeur de «retour»?
james emanon
1
Consumer <Integer, Integer> n'est pas valide. Un consommateur a un seul paramètre de type.
nascar
2
Mais POURQUOI créer une telle construction? Quel problème est résolu en le disposant en Java?
Tronc
177

La raison pour laquelle vous avez du mal à saisir la signification d'interfaces fonctionnelles telles que celles de java.util.functionest que les interfaces définies ici n'ont aucune signification! Ils sont présents principalement pour représenter la structure , pas la sémantique .

Ceci est atypique pour la plupart des API Java. L'API Java typique, telle qu'une classe ou une interface, a une signification, et vous pouvez développer un modèle mental pour ce qu'elle représente et l'utiliser pour comprendre les opérations sur elle. Considérez java.util.Listpar exemple. A Listest un conteneur d'autres objets. Ils ont une séquence et un index. Le nombre d'objets contenus dans la liste est renvoyé par size(). Chaque objet a un index dans la plage 0..size-1 (inclus). L'objet à l'index i peut être récupéré en appelant list.get(i). Et ainsi de suite.

Les interfaces fonctionnelles dans java.util.functionn'ont pas une telle signification. Au lieu de cela, ce sont des interfaces qui représentent simplement la structure d'une fonction, comme le nombre d'arguments, le nombre de valeurs de retour et (parfois) si un argument ou une valeur de retour est une primitive. Nous avons donc quelque chose comme ce Function<T,R>qui représente une fonction qui prend un seul argument de type T et renvoie une valeur de type R . C'est tout. Que fait cette fonction? Eh bien, il peut tout faire ... du moment qu'il prend un seul argument et renvoie une seule valeur. C'est pourquoi la spécification pour Function<T,R>est un peu plus que «Représente une fonction qui accepte un argument et produit un résultat».

De toute évidence, lorsque nous écrivons du code, il a un sens, et ce sens doit venir de quelque part. Dans le cas des interfaces fonctionnelles, la signification vient du contexte dans lequel elles sont utilisées. L'interface Function<T,R>n'a aucune signification isolée. Cependant, dans l' java.util.Map<K,V>API, il y a ce qui suit:

V computeIfAbsent(K key, Function<K,V> mappingFunction)

(les jokers élidés par souci de brièveté)

Ah, cette utilisation de Functionest une "fonction de cartographie". Qu'est-ce que cela fait? Dans ce contexte, si keyn'est pas déjà présente dans la carte, la fonction de mappage est appelée et reçoit la clé et devrait produire une valeur, et la paire clé-valeur résultante est insérée dans la carte.

Vous ne pouvez donc pas regarder la spécification pour Function(ou l'une des autres interfaces fonctionnelles, d'ailleurs) et essayer de discerner ce qu'elles signifient. Vous devez regarder où elles sont utilisées dans d'autres API pour comprendre ce qu'elles signifient, et cette signification ne s'applique qu'à ce contexte.

Marques Stuart
la source
3
Donc en gros, c'est juste une fonction de type
JGuo
Une autre information utile peut être que les interfaces fonctionnelles peuvent avoir plusieurs méthodes implémentées qui peuvent ajouter un comportement à votre code
Jhon Mario Lotero
28

A Supplierest toute méthode qui ne prend aucun argument et renvoie une valeur. Son travail est littéralement de fournir une instance d'une classe attendue. Par exemple, chaque référence à une méthode «getter» est unSupplier

public Integer getCount(){
    return this.count;
}

Sa référence de méthode d'instance myClass::getCountest une instance de Supplier<Integer>.

A Consumerest toute méthode qui prend des arguments et ne renvoie rien. Il est invoqué pour ses effets secondaires. En termes Java, a Consumerest un idiome pour une voidméthode. Les méthodes 'setter' sont un bon exemple:

public void setCount(int count){
    this.count = count;
}

Sa référence de méthode d'instance myClass::setCountest une instance de Consumer<Integer>et IntConsumer.

A Function<A,B>est toute méthode qui prend un argument d'un type et en renvoie un autre. Cela peut être qualifié de «transformation». Le Function<A,B>prend un Aet renvoie un B. Il convient de noter que pour une valeur donnée de A, la fonction doit toujours renvoyer une valeur spécifique de B. Aet Bpeut en fait être du même type, comme le suivant:

public Integer addTwo(int i){
    return i+2;
}

Sa référence de méthode d'instance myClass:addTwoest a Function<Integer, Integer>et a ToIntFunction<Integer>.

Une référence de méthode Class à un getter est un autre exemple de fonction.

public Integer getCount(){
    return this.count;
}

Sa référence de méthode de classe MyClass::getCountest une instance de Function<MyClass,Integer>et ToIntFunction<MyClass>.

Steve K
la source
15

Pourquoi les consommateurs / fournisseurs / autres interfaces fonctionnelles définies dans le paquet de java.util.function : Consommateur et fournisseur sont deux, parmi d' autres, des interfaces fonctionnelles en construction fournies en Java 8. Le but de toutes ces interfaces fonctionnelles en construction est pour fournir un «modèle» prêt pour les interfaces fonctionnelles ayant des descripteurs de fonction communs (signatures / définitions de méthode fonctionnelle).

Disons que nous avons un besoin pour convertir un type T en un autre type R. Si nous devions passer une fonction définie comme celle-ci en tant que paramètre à une méthode, alors cette méthode aurait besoin de définir une interface fonctionnelle dont la méthode fonctionnelle / abstraite prend le paramètre de type T en entrée et donne un paramètre de type R en sortie. Maintenant, il pourrait y avoir de nombreux scénarios comme celui-ci et le (s) programmeur (s) finiraient par définir plusieurs interfaces fonctionnelles pour leurs besoins. Pour éviter ce genre de scénario, faciliter la programmation et apporter une norme commune dans l'utilisation des interfaces fonctionnelles, un ensemble d'interfaces fonctionnelles intégrées telles que prédicat, fonction, consommateur et fournisseur ont été définis.

Que fait le consommateur : L'interface fonctionnelle du consommateur accepte une entrée, fait quelque chose avec cette entrée et ne donne aucune sortie. Sa définition est comme celle-ci (de Java Source) -

@FunctionalInterface
public interface Consumer<T> {
 void accept(T t);
}

Ici, accept () est la méthode fonctionnelle \ abstraite qui prend une entrée et ne renvoie aucune sortie. Donc, si vous voulez entrer un Integer, faites quelque chose avec sans sortie puis au lieu de définir votre propre interface, utilisez une instance de Consumer.

Que fait le fournisseur : L'interface fonctionnelle du fournisseur ne prend aucune entrée mais renvoie une sortie. Il est défini comme ceci (à partir de Java Source) -

@FunctionalInterface
public interface Supplier<T> {
  T get();
}

Partout où vous avez besoin d'une fonction qui renvoie quelque chose, disons un entier, mais ne prend aucune sortie, utilisez une instance de Supplier.

Dans le cas où plus de clarté, ainsi que des exemples d'utilisation, des interfaces consommateur et fournisseur sont nécessaires, vous pouvez consulter mes articles de blog sur le même - http://www.javabrahman.com/java-8/java-8-java-util- function-consumer-tutorial-with-examples / et http://www.javabrahman.com/java-8/java-8-java-util-function-supplier-tutorial-with-examples/

Dhruv Rai Puri
la source
12

1. Signification

Voir mes réponses à ma question ici et à une autre ici , mais en bref, ces nouvelles interfaces fournissent une convention et un caractère descriptif pour que tout le monde puisse les utiliser (+ chaînage de méthodes funky comme.forEach(someMethod().andThen(otherMethod()))

2. Différences

Consommateur : prend quelque chose, fait quelque chose, ne renvoie rien:void accept(T t)

Fournisseur: Ne prend rien, retourne quelque chose: T get()(inverse de Consumer, essentiellement une méthode `` getter '' universelle)

3. Utilisation

// Consumer: It takes something (a String) and does something (prints it) 
    List<Person> personList = getPersons();

     personList.stream()
                    .map(Person::getName)    
                    .forEach(System.out::println); 

Fournisseur: enroulez le code répétitif, par exemple la synchronisation d'exécution du code

public class SupplierExample {

    public static void main(String[] args) {

        // Imagine a class Calculate with some methods
        Double result1 = timeMe(Calculate::doHeavyComputation);
        Double result2 = timeMe(Calculate::doMoreComputation);
    }
    private static Double timeMe(Supplier<Double> code) {

        Instant start = Instant.now();
        // Supplier method .get() just invokes whatever it is passed
        Double result = code.get();
        Instant end = Instant.now();

        Duration elapsed = Duration.between(start,end);
        System.out.println("Computation took:" + elapsed.toMillis());

        return result;
    }
}
Andrejs
la source
0

En termes profanes,

le fournisseur fournira des données mais sans consommer aucune donnée. En termes de programmation, une méthode qui ne prend aucun argument mais renvoie une valeur. Il est utilisé pour générer de nouvelles valeurs.

http://codedestine.com/java-8-supplier-interface/

Le consommateur consommera des données et ne retournera aucune donnée. En termes de programmation, une méthode qui prend plusieurs arguments et ne renvoie aucune valeur.

http://codedestine.com/java-8-consumer-interface/

lalitbhagtani
la source
0

Le consommateur et le fournisseur sont les interfaces fournies par java. Le consommateur est utilisé pour itérer sur les éléments de la liste et le fournisseur est utilisé pour l'objet d'approvisionnement

vous pouvez facilement comprendre avec la démonstration de code.

Consommateur

package com.java.java8;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;

/**
 * The Class ConsumerDemo.
 *
 * @author Ankit Sood Apr 20, 2017
 */
public class ConsumerDemo {

    /**
     * The main method.
     *
     * @param args
     *            the arguments
     */
    public static void main(String[] args) {

    List<String> str = new ArrayList<>();
    str.add("DEMO");
    str.add("DEMO2");
    str.add("DEMO3");

    /* Consumer is use for iterate over the List */
    Consumer<String> consumer = new Consumer<String>() {
        @Override
        public void accept(String t) {

        /* Print list element on consile */
        System.out.println(t);
        }
    };

    str.forEach(consumer);

    }

}

Fournisseur

package com.java.java8;

import java.util.function.Supplier;

/**
 * The Class SupplierDemo.
 *
 * @author Ankit Sood Apr 20, 2017
 */
public class SupplierDemo {

    /**
     * The main method.
     *
     * @param args
     *            the arguments
     */
    public static void main(String[] args) {
    getValue(() -> "Output1");
    getValue(() -> "OutPut2");
    }

    /**
     * Gets the value.
     *
     * @param supplier
     *            the supplier
     * @return the value
     */
    public static void getValue(Supplier<?> supplier) {
    System.out.println(supplier.get());
    }

}
Ankit Sood
la source
0

La réponse la plus simple peut être:

Un consommateur peut être considéré comme une fonction <T, Void>. Un fournisseur peut être considéré comme une fonction <Void, T>.

Yashwin Munsadwala
la source