Java 8 Spiegazione del fornitore e del consumatore per il laico


101

In qualità di programmatore non Java che impara Java, sto leggendo informazioni Suppliere Consumerinterfacce in questo momento. E non riesco a capire il loro utilizzo e significato. Quando e perché useresti queste interfacce? Qualcuno può darmi un semplice esempio da profano di questo ... Trovo gli esempi di Doc non abbastanza succinti per la mia comprensione.


4
Ogni pagina del documento API ha un collegamento etichettato "USE" nella parte superiore su cui puoi fare clic Consumere Supplierpuoi anche cercare nel tutorial per Consumer...
Holger,

7
Adoro la risposta di Stuart Marks. E penso che la maggior parte delle persone che hanno risposto di seguito abbiano perso il punto. La domanda non è "come" scrivere Fornitori, Consumatori e Funzioni. È "perché" nel mondo vorresti? Per una persona che non è abituata a loro, rendono il codice molto più complesso. Ma il vantaggio di usarli non è chiaro.
anton1980

Per quanto posso vedere (e condivido la tua frustrazione con le descrizioni tangenziali) è solo un modo astuto di astrarre sia il tipo di oggetto che il trattamento dell'oggetto da un oggetto usato in un pezzo di codice. Ciò consente l'applicazione di questo stesso codice a molti diversi tipi di oggetto semplicemente definendo diverse nuove classi e inserendole nelle interfacce Fornitore e Consumatore. Quindi, in un sistema di casellario della polizia, lo stesso codice superficiale viene utilizzato per tutti i sospettati, ma la stampa finale per ciascuno dipende dalla classificazione di ciascun sospetto, ad esempio "cittadino", "meschino", "ladro", "criminale", "indurito", ecc.
Trunk

Risposte:


95

Questo è il fornitore:

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

Questo è Consumer:

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

Quindi, in parole povere, un fornitore è un metodo che restituisce un valore (come nel suo valore di ritorno). Considerando che, un consumatore è un metodo che consuma un certo valore (come nell'argomento del metodo) e fa alcune operazioni su di essi.

Quelli si trasformeranno in qualcosa di simile a questi:

// 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);

Per quanto riguarda l'utilizzo, l'esempio di base sarebbe: Stream#forEach(Consumer)metodo. Richiede un consumatore, che consuma l'elemento dal flusso su cui stai iterando ed esegue alcune azioni su ciascuno di essi. Probabilmente stampali.

Consumer<String> stringConsumer = (s) -> System.out.println(s.length());
Arrays.asList("ab", "abc", "a", "abcd").stream().forEach(stringConsumer);

3
Quindi, un fornitore è un modo per creare un'istanza di un metodo che restituisce "qualcosa"?
james emanon

3
@jamesemanon Esattamente. Potrebbe essere un riferimento a un metodo o un lambda.
Rohit Jain

14
Qual è il vantaggio di questo piuttosto che chiamare direttamente il metodo? È perché il Fornitore può agire come un intermediario e trasferire quel valore di "ritorno"?
james emanon

1
Consumer <Integer, Integer> non è valido. Un consumatore ha un unico parametro di tipo.
nascar

2
Ma PERCHÉ creare un tale costrutto? Quale problema viene risolto avendolo in Java?
Trunk

178

Il motivo per cui hai difficoltà a cogliere il significato di interfacce funzionali come quelle in java.util.functionè che le interfacce qui definite non hanno alcun significato! Sono presenti principalmente per rappresentare la struttura , non la semantica .

Questo è atipico per la maggior parte delle API Java. La tipica API Java, come una classe o un'interfaccia, ha un significato e puoi sviluppare un modello mentale per ciò che rappresenta e usarlo per comprendere le operazioni su di esso. Considera java.util.Listper esempio. A Listè un contenitore di altri oggetti. Hanno una sequenza e un indice. Il numero di oggetti contenuti nell'elenco viene restituito da size(). Ogni oggetto ha un indice nell'intervallo 0..size-1 (incluso). L'oggetto all'indice i può essere recuperato chiamando list.get(i). E così via.

Le interfacce funzionali in java.util.functionnon hanno questo significato. Sono invece interfacce che rappresentano semplicemente la struttura di una funzione, come il numero di argomenti, il numero di valori restituiti e (a volte) se un argomento o il valore restituito è una primitiva. Così abbiamo qualcosa di simile Function<T,R>che rappresenta una funzione che prende un singolo parametro di tipo T e restituisce un valore di tipo R . Questo è tutto. Cosa fa quella funzione? Bene, può fare qualsiasi cosa ... purché richieda un singolo argomento e restituisca un singolo valore. Ecco perché la specifica per Function<T,R>è poco più di "Rappresenta una funzione che accetta un argomento e produce un risultato".

Chiaramente, quando scriviamo codice, ha un significato e quel significato deve provenire da qualche parte. Nel caso delle interfacce funzionali, il significato deriva dal contesto in cui vengono utilizzate. L'interfaccia Function<T,R>non ha significato da sola. Tuttavia, java.util.Map<K,V>nell'API, c'è quanto segue:

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

(caratteri jolly elisi per brevità)

Ah, questo uso di Functionè come una "funzione di mappatura". Cosa fa? In questo contesto, se keynon è già presente nella mappa, viene chiamata la funzione di mappatura e viene consegnata la chiave e si prevede che produca un valore, e la coppia chiave-valore risultante viene inserita nella mappa.

Quindi non puoi guardare le specifiche per Function(o qualsiasi altra interfaccia funzionale, se è per questo) e cercare di discernere cosa significano. Devi guardare dove vengono utilizzati in altre API per capire cosa significano e quel significato si applica solo a quel contesto.


3
Quindi, in pratica, funziona solo come tipo
JGuo

Può essere un'altra informazione utile che le interfacce funzionali possono avere più metodi implementati che possono aggiungere comportamenti al tuo codice
Jhon Mario Lotero

28

A Supplierè un metodo che non accetta argomenti e restituisce un valore. Il suo compito è letteralmente fornire un'istanza di una classe prevista. Ad esempio, ogni riferimento a un metodo "getter" è un fileSupplier

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

Il suo riferimento al metodo di istanza myClass::getCountè un'istanza di Supplier<Integer>.

A Consumerè un metodo che accetta argomenti e non restituisce nulla. Viene invocato per i suoi effetti collaterali. In termini Java, a Consumerè un idioma per un voidmetodo. I metodi 'setter' sono un buon esempio:

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

Il suo riferimento al metodo di istanza myClass::setCountè un'istanza di Consumer<Integer>e IntConsumer.

A Function<A,B>è qualsiasi metodo che accetta un argomento di un tipo e ne restituisce un altro. Questa può essere definita una "trasformazione". Il Function<A,B>prende un Ae restituisce a B. È interessante notare che per un dato valore di A, la funzione dovrebbe sempre restituire un valore specifico di B. Ae Bpuò infatti essere dello stesso tipo, come il seguente:

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

Il suo riferimento al metodo di istanza myClass:addTwoè a Function<Integer, Integer>e a ToIntFunction<Integer>.

Un riferimento al metodo Class a un getter è un altro esempio di funzione.

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

Il suo riferimento al metodo di classe MyClass::getCountè un'istanza di Function<MyClass,Integer>e ToIntFunction<MyClass>.


15

Perché Consumatore / Fornitore / altre interfacce funzionali sono definite nel pacchetto java.util.function : Consumatore e Fornitore sono due, tra le tante, interfacce funzionali integrate fornite in Java 8. Lo scopo di tutte queste interfacce funzionali integrate è fornire un "modello" pronto per interfacce funzionali aventi descrittori di funzioni comuni (firme / definizioni di metodi funzionali).

Diciamo che abbiamo l'obbligo di convertire un tipo T in un altro tipo R. Se dovessimo passare una funzione definita in questo modo come parametro a un metodo, quel metodo dovrebbe definire un'interfaccia funzionale il cui metodo funzionale / astratto prende parametro di tipo T come input e fornisce un parametro di tipo R come output. Ora, potrebbero esserci molti scenari come questo e i programmatori finirebbero per definire più interfacce funzionali per le loro esigenze. Per evitare questo tipo di scenario, facilitare la programmazione e portare uno standard comune nell'utilizzo delle interfacce funzionali, è stata definita una serie di interfacce funzionali integrate come Predicato, Funzione, Consumatore e Fornitore.

Cosa fa il consumatore : l'interfaccia funzionale del consumatore accetta un input, fa qualcosa con quell'input e non fornisce alcun output. La sua definizione è così (da Java Source) -

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

Qui accept () è il metodo funzionale \ abstract che accetta un input e non restituisce alcun output. Quindi, se vuoi inserire un numero intero, fai qualcosa con esso senza output, quindi invece di definire la tua interfaccia usa un'istanza di Consumer.

Cosa fa il fornitore : l'interfaccia funzionale del fornitore non accetta input ma restituisce un output. È definito in questo modo (da Java Source) -

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

Ovunque sia necessaria una funzione che restituisca qualcosa, ad esempio un numero intero, ma non riceva output, utilizzare un'istanza di Supplier.

Nel caso in cui sia necessaria maggiore chiarezza, insieme all'uso di esempio, delle interfacce del consumatore e del fornitore, puoi fare riferimento ai miei post sul blog sullo stesso - http://www.javabrahman.com/java-8/java-8-java-util- function-consumer-tutorial-with-examples / e http://www.javabrahman.com/java-8/java-8-java-util-function-supplier-tutorial-with-examples/


12

1. Significato

Vedi le mie risposte alla mia domanda qui e anche un'altra qui , ma in breve queste nuove interfacce forniscono convenzione e descrittività per tutti da usare (+ concatenamento di metodi funky come.forEach(someMethod().andThen(otherMethod()))

2. Differenze

Consumatore : prende qualcosa, fa qualcosa, non restituisce nulla:void accept(T t)

Fornitore: non prende nulla, restituisce qualcosa: T get()(al contrario di Consumer, fondamentalmente un metodo "getter" universale)

3. Utilizzo

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

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

Fornitore: avvolgere il codice ripetitivo, ad esempio i tempi di esecuzione del codice

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;
    }
}

0

In termini laici,

il fornitore fornirà i dati ma senza consumare alcun dato. In termini di programmazione un metodo che non accetta argomenti ma restituisce un valore. Viene utilizzato per generare nuovi valori.

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

il consumatore consumerà i dati ma non restituirà alcun dato. In termini di programmazione un metodo che accetta più argomenti e non restituisce alcun valore.

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


0

Consumatore e fornitore sono le interfacce fornite da java. Il consumatore è utilizzato per l'iterazione sugli elementi dell'elenco e il fornitore è utilizzato per gli oggetti di fornitura

puoi facilmente capire con la dimostrazione del codice.

Consumatore

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);

    }

}

Fornitore

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());
    }

}

0

La risposta più semplice può essere:

Un consumatore può essere visto come una funzione <T, Void>. Un fornitore può essere visto come una funzione <Void, T>.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.