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.

James Emanon
źródło
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);
Rohit Jain
źródło
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.

Stuart Marks
źródło
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>.

Steve K.
źródło
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/

Dhruv Rai Puri
źródło
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;
    }
}
Andrejs
źródło
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/

lalitbhagtani
źródło
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());
    }

}
Ankit Sood
źródło
0

Najprostszą odpowiedzią może być:

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

Yashwin Munsadwala
źródło