Java 8 Lieferanten- und Verbrauchererklärung für den Laien

99

Als Nicht-Java-Programmierer, der Java lernt, lese ich gerade über Supplierund ConsumerSchnittstellen. Und ich kann mich nicht um ihre Verwendung und Bedeutung kümmern. Wann und warum würden Sie diese Schnittstellen verwenden? Kann mir jemand ein einfaches Beispiel für Laien geben? Ich finde die Doc-Beispiele nicht prägnant genug für mein Verständnis.

James Emanon
quelle
4
Jede Seite des API - Doc hat ein Link „USE“ an der Spitze markiert , die Sie klicken können , Consumerund SupplierSie können auch die Suche Tutorial für Consumer...
Holger
7
Ich liebe die Antwort von Stuart Marks. Und ich denke, die meisten Leute, die unten geantwortet haben, haben den Punkt verpasst. Die Frage ist nicht, wie Lieferanten, Verbraucher und Funktionen zu schreiben sind. Es ist "warum" in der Welt möchten Sie? Für eine Person, die nicht an sie gewöhnt ist, machen sie den Code viel komplexer. Der Nutzen ihrer Verwendung ist jedoch nicht klar.
anton1980
Soweit ich sehen kann (und ich teile Ihre Frustration mit den tangentialen Beschreibungen), ist dies nur eine raffinierte Methode, um sowohl den Objekttyp als auch die Objektbehandlung von einem in einem Code verwendeten Objekt zu abstrahieren. Dies ermöglicht die Anwendung desselben Codes auf viele verschiedene Objekttypen, indem einfach verschiedene neue Klassen definiert und in die Lieferanten- und Verbraucherschnittstellen eingefügt werden. In einem Polizeiaufzeichnungssystem wird für alle Verdächtigen derselbe oberflächliche Code verwendet, aber der endgültige Ausdruck für jeden Verdächtigen hängt von der Klassifizierung jedes Verdächtigen ab, z. B. "Bürger", "Kleinlich", "Diebstahl", "Schwerverbrecher", "verhärtet". usw.
Kofferraum

Antworten:

95

Dies ist Lieferant:

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

Dies ist Verbraucher:

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

Für Laien ist ein Lieferant eine Methode, die einen bestimmten Wert zurückgibt (wie im Rückgabewert). Während ein Verbraucher eine Methode ist, die einen bestimmten Wert verbraucht (wie im Methodenargument) und einige Operationen an ihnen ausführt.

Diese werden sich in so etwas verwandeln:

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

Das grundlegende Beispiel für die Verwendung wäre: Stream#forEach(Consumer)Methode. Es wird ein Consumer benötigt, der das Element aus dem Stream verbraucht, auf dem Sie iterieren, und für jeden von ihnen eine Aktion ausführt. Drucken Sie sie wahrscheinlich aus.

Consumer<String> stringConsumer = (s) -> System.out.println(s.length());
Arrays.asList("ab", "abc", "a", "abcd").stream().forEach(stringConsumer);
Rohit Jain
quelle
3
Ein Lieferant ist also eine Möglichkeit, eine Instanz einer Methode zu erstellen, die "etwas" zurückgibt?
James Emanon
3
@ Jamesemanon Genau. Das könnte eine Methodenreferenz oder ein Lambda sein.
Rohit Jain
14
Was ist der Vorteil davon, anstatt die Methode direkt aufzurufen? Liegt es daran, dass der Lieferant wie ein Vermittler handeln und diesen "Rückgabewert" abgeben kann?
James Emanon
1
Consumer <Integer, Integer> ist ungültig. Ein Verbraucher hat einen einzelnen Typparameter.
Nascar
2
Aber WARUM ein solches Konstrukt erstellen? Welches Problem wird durch Java gelöst?
Kofferraum
175

Der Grund, warum Sie Schwierigkeiten haben, die Bedeutung funktionaler Schnittstellen wie der in zu java.util.functionverstehen, ist, dass die hier definierten Schnittstellen keine Bedeutung haben! Sie sind hauptsächlich vorhanden, um die Struktur darzustellen , nicht die Semantik .

Dies ist für die meisten Java-APIs untypisch. Die typische Java-API, z. B. eine Klasse oder Schnittstelle, hat eine Bedeutung, und Sie können ein mentales Modell für das, was es darstellt, entwickeln und dieses verwenden, um die Operationen darauf zu verstehen. Betrachten Sie java.util.Listzum Beispiel. A Listist ein Container mit anderen Objekten. Sie haben eine Sequenz und einen Index. Die Anzahl der in der Liste enthaltenen Objekte wird von zurückgegeben size(). Jedes Objekt hat einen Index im Bereich 0..size-1 (einschließlich). Das Objekt am Index i kann durch Aufrufen abgerufen werden list.get(i). Und so weiter.

Die funktionalen Schnittstellen in java.util.functionhaben keine solche Bedeutung. Stattdessen handelt es sich um Schnittstellen, die lediglich die Struktur einer Funktion darstellen, z. B. die Anzahl der Argumente, die Anzahl der Rückgabewerte und (manchmal), ob ein Argument oder ein Rückgabewert ein Grundelement ist. Wir haben also so etwas wie Function<T,R>eine Funktion, die ein einzelnes Argument vom Typ T verwendet und einen Wert vom Typ R zurückgibt . Das ist es. Was macht diese Funktion? Nun, es kann alles ... solange es ein einzelnes Argument benötigt und einen einzelnen Wert zurückgibt. Aus diesem Grund ist die Angabe für Function<T,R>kaum mehr als "Stellt eine Funktion dar, die ein Argument akzeptiert und ein Ergebnis erzeugt."

Wenn wir Code schreiben, hat er eindeutig eine Bedeutung, und diese Bedeutung muss von irgendwoher kommen. Bei den Funktionsschnittstellen ergibt sich die Bedeutung aus dem Kontext, in dem sie verwendet werden. Die Schnittstelle Function<T,R>hat für sich genommen keine Bedeutung. In der java.util.Map<K,V>API gibt es jedoch Folgendes:

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

(Platzhalter wurden der Kürze halber entfernt)

Ah, diese Verwendung Functionist als "Mapping-Funktion". Was macht das? Wenn in diesem Zusammenhang keynicht bereits in der Karte vorhanden ist, wird die Zuordnungsfunktion aufgerufen und der Schlüssel übergeben, und es wird erwartet, dass ein Wert erzeugt wird, und das resultierende Schlüssel-Wert-Paar wird in die Karte eingefügt.

Sie können sich also nicht die Spezifikation Function(oder eine der anderen funktionalen Schnittstellen) ansehen und versuchen, zu erkennen, was sie bedeuten. Sie müssen sich ansehen, wo sie in anderen APIs verwendet werden, um zu verstehen, was sie bedeuten. Diese Bedeutung gilt nur für diesen Kontext.

Stuart Marks
quelle
3
Im Grunde genommen funktioniert es nur als Typ
JGuo
Eine weitere nützliche Information kann sein, dass funktionale Schnittstellen mehrere implementierte Methoden haben können, die Ihrem Code Verhalten hinzufügen können
Jhon Mario Lotero
28

A Supplierist eine Methode, die keine Argumente akzeptiert und einen Wert zurückgibt. Seine Aufgabe ist es buchstäblich, eine Instanz einer erwarteten Klasse bereitzustellen. Zum Beispiel ist jeder Verweis auf eine 'Getter'-Methode aSupplier

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

Die Instanzmethodenreferenz myClass::getCountist eine Instanz von Supplier<Integer>.

A Consumerist eine Methode, die Argumente akzeptiert und nichts zurückgibt. Es wird wegen seiner Nebenwirkungen angerufen. In Java Consumerist a eine Redewendung für eine voidMethode. Setter-Methoden sind ein gutes Beispiel:

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

Die Instanzmethodenreferenz myClass::setCountist eine Instanz von Consumer<Integer>und IntConsumer.

A Function<A,B>ist eine Methode, die ein Argument eines Typs verwendet und ein anderes zurückgibt. Dies kann als "Transformation" bezeichnet werden. Das Function<A,B>nimmt ein Aund gibt ein zurück B. Bemerkenswert ist, dass Adie Funktion für einen bestimmten Wert von immer einen bestimmten Wert von zurückgeben sollte B. Aund Bkann in der Tat der gleiche Typ sein, wie der folgende:

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

Die Instanzmethodenreferenz myClass:addTwolautet a Function<Integer, Integer>und a ToIntFunction<Integer>.

Eine Klassenmethodenreferenz auf einen Getter ist ein weiteres Beispiel für eine Funktion.

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

Die Klassenmethodenreferenz MyClass::getCountist eine Instanz von Function<MyClass,Integer>und ToIntFunction<MyClass>.

Steve K.
quelle
15

Warum sind Consumer / Supplier / andere funktionale Schnittstellen im Funktionspaket java.util.function definiert : Consumer und Supplier sind zwei von vielen der in Java 8 bereitgestellten integrierten funktionalen Schnittstellen. Der Zweck all dieser integrierten funktionalen Schnittstellen ist Bereitstellung einer fertigen "Vorlage" für funktionale Schnittstellen mit gemeinsamen Funktionsdeskriptoren (Signaturen / Definitionen für funktionale Methoden).

Nehmen wir an, wir müssen einen Typ T in einen anderen Typ R konvertieren. Wenn wir einen übergeben würden so definierte Funktion als Parameter an eine Methode übergeben würden, müsste diese Methode eine funktionale Schnittstelle definieren, deren funktionale / abstrakte Methode Parameter akzeptiert vom Typ T als Eingabe und gibt einen Parameter vom Typ R als Ausgabe an. Nun könnte es viele Szenarien wie diese geben, und die Programmierer würden am Ende mehrere funktionale Schnittstellen für ihre Anforderungen definieren. Um diese Art von Szenario zu vermeiden, die Programmierung zu vereinfachen und einen gemeinsamen Standard für die Verwendung von Funktionsschnittstellen einzuführen, wurden eine Reihe integrierter Funktionsschnittstellen wie Prädikat, Funktion, Verbraucher und Lieferant definiert.

Was macht Consumer? Die Consumer-Funktionsschnittstelle akzeptiert eine Eingabe, macht etwas mit dieser Eingabe und gibt keine Ausgabe aus. Seine Definition ist wie folgt (aus Java Source) -

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

Hier ist accept () die funktionale \ abstract-Methode, die eine Eingabe akzeptiert und keine Ausgabe zurückgibt. Wenn Sie also eine Ganzzahl eingeben möchten, tun Sie etwas damit ohne Ausgabe. Verwenden Sie statt der Definition Ihrer eigenen Schnittstelle eine Instanz von Consumer.

Was macht der Lieferant? Die funktionale Schnittstelle des Lieferanten nimmt keine Eingabe entgegen, sondern gibt eine Ausgabe zurück. Es ist wie folgt definiert (aus Java Source) -

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

Überall dort, wo Sie eine Funktion benötigen, die etwas zurückgibt, beispielsweise eine Ganzzahl, aber keine Ausgabe verwendet, verwenden Sie eine Instanz von Supplier.

Wenn mehr Klarheit und eine beispielhafte Verwendung der Benutzer- und Lieferantenschnittstellen erforderlich sind, können Sie meine Blog-Beiträge auf dieselbe verweisen - http://www.javabrahman.com/java-8/java-8-java-util- Funktion-Verbraucher-Tutorial-mit-Beispielen / und http://www.javabrahman.com/java-8/java-8-java-util-function-supplier-tutorial-with-examples/

Dhruv Rai Puri
quelle
12

1. Bedeutung

Sehen Sie meine Antworten auf meine Frage hier und auch eine andere hier , aber kurz gesagt, diese neuen Schnittstellen bieten Konventionen und Beschreibungen für alle (+ funky Methodenverkettung wie z.forEach(someMethod().andThen(otherMethod()))

2. Unterschiede

Verbraucher : Nimmt etwas, tut etwas, gibt nichts zurück:void accept(T t)

Lieferant: Nimmt nichts, gibt etwas zurück: T get()(Umkehrung des Verbrauchers, im Grunde eine universelle 'Getter'-Methode)

3. Verwendung

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

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

Lieferant: Wrap repetitiver Code, z. B. Timing der Codeausführung

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
quelle
0

In Laienbegriffen,

Der Lieferant liefert Daten, ohne jedoch Daten zu verbrauchen. In Programmierbegriffen eine Methode, die kein Argument akzeptiert, sondern einen Wert zurückgibt. Es wird verwendet, um neue Werte zu generieren.

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

Der Verbraucher verbraucht Daten und gibt keine Daten zurück. In Programmierbegriffen eine Methode, die mehrere Argumente akzeptiert und keinen Wert zurückgibt.

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

lalitbhagtani
quelle
0

Verbraucher und Lieferant sind die von Java bereitgestellten Schnittstellen. Der Verbraucher wird zum Durchlaufen der Listenelemente verwendet, und der Lieferant wird zum Bereitstellen von Lieferobjekten verwendet

Sie können mit Code-Demonstration leicht verstehen.

Verbraucher

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

    }

}

Lieferant

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
quelle
0

Die einfachste Antwort kann sein:

Ein Verbraucher kann als Funktion <T, Void> angesehen werden. Ein Lieferant kann als Funktion <Void, T> angesehen werden.

Yashwin Munsadwala
quelle
0

Zusamenfassend,

  1. Lieferant

stellt eine anonyme Funktion dar, die kein Argument akzeptiert und ein Ergebnis erzeugt.

  1. Verbraucher

stellt eine anonyme Funktion dar, die ein Argument akzeptiert und kein Ergebnis liefert.

Es gibt auch ein Prädikat . Das Prädikat stellt eine anonyme Funktion dar, die ein Argument akzeptiert und ein Ergebnis erzeugt.

Surbhi
quelle