Wyjaśnienie dostawcy i konsumenta Java 8 dla laika


102

Jako programista nie Java nauki Java, czytam o Supplieri Consumerinterfejsy w tej chwili. I nie mogę pojąć ich użycia i znaczenia. Kiedy i dlaczego miałbyś używać tych interfejsów? Czy ktoś może mi podać prosty przykład dla laika… Uważam, że przykłady Doca nie są wystarczająco zwięzłe, aby moje zrozumienie.


4
Każda strona Doc API link oznaczony „użytkowania” na górze które można kliknąć na Consumeri Suppliermoże też przeszukać poradnik dla Consumer...
— Holger

7
Uwielbiam odpowiedź Stuarta Marksa. I myślę, że większość osób, które odpowiedziały poniżej, nie zrozumiała sedna. Pytanie nie brzmi: „jak” pisać dostawców, konsumentów i funkcje. To „dlaczego” na świecie miałbyś chcieć? Dla osoby, która nie jest do nich przyzwyczajona, znacznie komplikują kod. Ale korzyści z ich używania nie są jasne.
— anton1980

O ile widzę (i podzielam twoją frustrację z opisami stycznymi) jest to po prostu sprytny sposób na wyodrębnienie zarówno typu obiektu, jak i traktowania obiektu z obiektu używanego w kawałku kodu. Pozwala to na zastosowanie tego samego kodu do wielu różnych typów obiektów poprzez proste zdefiniowanie różnych nowych klas i wstrzyknięcie ich do interfejsów dostawcy i konsumenta. Tak więc w policyjnym systemie rejestrów ten sam powierzchowny kod jest używany do wszystkich podejrzanych, ale ostateczny wydruk każdego z nich zależy od klasyfikacji każdego podejrzanego, np. „Obywatel”, „drobny”, „larcen”, „przestępca”, „zahartowany”, itd.
— Trunk,

Odpowiedzi:


95

To jest Dostawca:

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

To jest konsument:

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

Więc w kategoriach laika dostawca jest metodą, która zwraca pewną wartość (jak w przypadku wartości zwracanej). Natomiast konsument jest metodą, która zużywa jakąś wartość (jak w argumencie metody) i wykonuje na nich pewne operacje.

Te zmienią się w coś takiego:

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

Jeśli chodzi o użycie, bardzo podstawowym przykładem będzie: Stream#forEach(Consumer)metoda. Pobiera Konsumenta, który zużywa element ze strumienia, po którym iterujesz, i wykonuje na każdym z nich pewną akcję. Prawdopodobnie je wydrukuje.

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

3
Zatem dostawca jest sposobem na utworzenie instancji metody, która zwraca „coś”?
— james emanon

3
@jamesemanon Dokładnie. Może to być odwołanie do metody lub lambda.
— Rohit Jain

15
Jaka jest korzyść z tego zamiast bezpośredniego wywoływania metody? Czy dzieje się tak dlatego, że Dostawca może działać jak pośrednik i przekazywać tę wartość „zwrotu”?
— james emanon

1
Konsument <Integer, Integer> jest nieprawidłowy. Konsument ma jeden parametr typu.
— nascar

2
Ale DLACZEGO stworzyć taki konstrukt? Jaki problem rozwiązuje posiadanie go w Javie?
— Trunk

179

Powodem, dla którego masz trudności ze zrozumieniem znaczenia funkcjonalnych interfejsów, takich jak te w, java.util.functionjest to, że zdefiniowane tutaj interfejsy nie mają żadnego znaczenia! Są obecne głównie w celu przedstawienia struktury , a nie semantyki .

Jest to nietypowe dla większości interfejsów API języka Java. Typowy interfejs API języka Java, taki jak klasa lub interfejs, ma znaczenie i można opracować model mentalny dla tego, co reprezentuje, i wykorzystać go do zrozumienia wykonywanych na nim operacji. Rozważmy java.util.Listna przykład. A Listto pojemnik z innymi przedmiotami. Mają sekwencję i indeks. Liczba obiektów zawartych na liście jest zwracana przez size(). Każdy obiekt ma indeks z zakresu 0..size-1 (włącznie). Obiekt pod indeksem i można pobrać wywołując list.get(i). I tak dalej.

Funkcjonalne interfejsy java.util.functionnie mają takiego znaczenia. Zamiast tego są interfejsami, które po prostu reprezentują strukturę funkcji, taką jak liczba argumentów, liczba zwracanych wartości i (czasami) to, czy argument lub wartość zwracana jest prymitywem. Tak więc mamy coś Function<T,R>co stanowi funkcję, która pobiera jeden argument typu T i zwraca wartość typu R . Otóż ​​to. Co robi ta funkcja? Cóż, może zrobić wszystko ... o ile przyjmuje pojedynczy argument i zwraca pojedynczą wartość. Dlatego specyfikacja for Function<T,R>to niewiele więcej niż „Reprezentuje funkcję, która przyjmuje jeden argument i generuje wynik”.

Oczywiście, kiedy piszemy kod, ma on znaczenie i to znaczenie musi skądś pochodzić. W przypadku funkcjonalnych interfejsów znaczenie wynika z kontekstu, w jakim są używane. Interfejs Function<T,R>nie ma żadnego znaczenia w izolacji. Jednak w java.util.Map<K,V>interfejsie API są następujące:

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

(dla zwięzłości pominięto symbole wieloznaczne)

Ach, to użycie Functionjest jako „funkcja mapowania”. Co to robi? W tym kontekście, jeśli keynie jest jeszcze obecna w mapie, wywoływana jest funkcja odwzorowania, otrzymuje klucz i oczekuje się, że wygeneruje wartość, a wynikowa para klucz-wartość jest wstawiana do mapy.

Nie możesz więc spojrzeć na specyfikację Function(lub żaden z innych funkcjonalnych interfejsów, jeśli o to chodzi) i spróbować rozróżnić, co one oznaczają. Musisz przyjrzeć się, gdzie są używane w innych interfejsach API, aby zrozumieć, co oznaczają, a znaczenie to dotyczy tylko tego kontekstu.


4
Zasadniczo działa tylko jako typ
— Jack Guo

Kolejną przydatną informacją może być to, że interfejsy funkcjonalne mogą mieć wiele zaimplementowanych metod, które mogą dodawać zachowanie do twojego kodu
— Jhon Mario Lotero

28

A Supplierto dowolna metoda, która nie przyjmuje argumentów i zwraca wartość. Jego zadaniem jest dosłownie dostarczenie instancji oczekiwanej klasy. Na przykład każde odwołanie do metody pobierającej to plikSupplier

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

Jego odwołanie do metody wystąpienia myClass::getCountjest wystąpieniem Supplier<Integer>.

A Consumerto dowolna metoda, która przyjmuje argumenty i nic nie zwraca. Jest wywoływany ze względu na skutki uboczne. W języku Java a Consumerjest idiomem dla voidmetody. Dobrym przykładem są metody „ustawiające”:

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

Jego odwołanie do metody wystąpienia myClass::setCountjest wystąpieniem Consumer<Integer>i IntConsumer.

A Function<A,B>to dowolna metoda, która przyjmuje argument jednego typu i zwraca inny. Można to nazwać „transformacją”. Function<A,B>Trwa Ai zwraca B. Warto zauważyć, że dla danej wartości Afunkcja powinna zawsze zwracać określoną wartość B. Ai Bmoże być w rzeczywistości tego samego typu, na przykład:

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

Jego odwołanie do metody wystąpienia myClass:addTwoto a Function<Integer, Integer>i a ToIntFunction<Integer>.

Odwołanie do metody klasy do metody pobierającej to kolejny przykład funkcji.

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

Jego odwołanie do metody klasy MyClass::getCountjest wystąpieniem Function<MyClass,Integer>i ToIntFunction<MyClass>.


15

Dlaczego w pakiecie java.util.function są zdefiniowane interfejsy konsumenta / dostawcy / inne interfejsy funkcjonalne : Konsument i Dostawca to dwa spośród wielu wbudowanych interfejsów funkcjonalnych dostępnych w Javie 8. Celem wszystkich wbudowanych interfejsów funkcjonalnych jest dostarczenie gotowego „szablonu” dla interfejsów funkcjonalnych posiadających wspólne deskryptory funkcji (sygnatury / definicje metod funkcjonalnych).

Powiedzmy, że mamy wymagane przekonwertowanie typu T na inny typ R. Jeśli mielibyśmy przekazać jakąkolwiek funkcję zdefiniowaną w ten sposób jako parametr do metody, wówczas ta metoda musiałaby zdefiniować interfejs funkcjonalny, którego metoda funkcjonalna / abstrakcyjna przyjmuje parametr typu T jako wejście i daje parametr typu R jako wyjście. Takich scenariuszy mogłoby być wiele i programiści mogliby w końcu zdefiniować wiele funkcjonalnych interfejsów dla swoich potrzeb. Aby uniknąć tego rodzaju scenariuszy, ułatwić programowanie i wprowadzić wspólny standard w korzystaniu z interfejsów funkcjonalnych, zdefiniowano zestaw wbudowanych interfejsów funkcjonalnych, takich jak predykat, funkcja, konsument i dostawca.

Co robi konsument : interfejs funkcjonalny konsumenta przyjmuje dane wejściowe, robi coś z tymi danymi wejściowymi i nie przekazuje żadnych danych wyjściowych. Jego definicja jest następująca (z Java Source) -

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

W tym przypadku accept () jest metodą funkcjonalną \ abstrakcyjną, która pobiera dane wejściowe i nie zwraca żadnego wyniku. Tak więc, jeśli chcesz wprowadzić liczbę całkowitą, zrób coś z nią bez wyjścia, a następnie zamiast definiować własny interfejs użyj instancji Consumer.

Co robi Dostawca : Funkcjonalny interfejs dostawcy nie przyjmuje żadnych danych wejściowych, ale zwraca dane wyjściowe. Jest tak zdefiniowany (z Java Source) -

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

Gdziekolwiek potrzebujesz funkcji, która coś zwraca, powiedz liczbę całkowitą, ale nie pobiera danych wyjściowych, użyj instancji Supplier.

W przypadku, gdy potrzebna jest większa przejrzystość, wraz z przykładowym wykorzystaniem, interfejsów konsumenta i dostawcy, możesz polecać moje posty na blogu na tym samym - http://www.javabrahman.com/java-8/java-8-java-util- function-customer-tutorial-with-examples / i http://www.javabrahman.com/java-8/java-8-java-util-function-supplier-tutorial-with-examples/


12

1. Znaczenie

Zobacz moje odpowiedzi na moje pytanie tutaj, a także inne tutaj , ale w skrócie te nowe interfejsy zapewniają konwencję i opisowość dla każdego do użycia (+ funky łańcuchowe metody, takie jak.forEach(someMethod().andThen(otherMethod()))

2. Różnice

Konsument : coś bierze, robi, nic nie zwraca:void accept(T t)

Dostawca: nic nie bierze, coś zwraca: T get()(odwrócenie konsumenta, w zasadzie uniwersalna metoda „pobierająca”)

3. Użytkowanie

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

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

Dostawca: opakuj powtarzalny kod, np. Czas wykonania kodu

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

W kategoriach laików

dostawca dostarczy dane, ale bez ich wykorzystywania. W terminologii programowania metoda, która nie przyjmuje żadnego argumentu, ale zwraca wartość. Służy do generowania nowych wartości.

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

konsument zużyje dane i nie zwróci żadnych danych. W terminologii programowania metoda, która przyjmuje wiele argumentów i nie zwraca żadnej wartości.

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


0

Konsument i dostawca to interfejsy dostarczane przez java. Konsument jest używany do iteracji po elementach listy, a dostawca jest używany do dostarczania obiektów

możesz łatwo zrozumieć dzięki demonstracji kodu.

Konsument

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

    }

}

Dostawca

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

Najprostszą odpowiedzią może być:

Konsument może być postrzegany jako Function <T, Void>. Dostawca może być postrzegany jako funkcja <Void, T>.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.