Объяснение поставщика и потребителя Java 8 для непрофессионала

103

Как программист не-Java обучения Java, я читал о Supplierи Consumerинтерфейсы в данный момент. И я не могу осмыслить их использование и значение. Когда и почему вы должны использовать эти интерфейсы? Может ли кто-нибудь дать мне простой пример для непрофессионала ... Я считаю, что примеры Doc недостаточно краткие для моего понимания.

Джеймс Эманон
источник
4
На каждой странице документа API есть ссылка с надписью «ИСПОЛЬЗОВАТЬ» вверху, на которую вы можете щелкнуть, Consumerи Supplierвы также можете выполнить поиск по руководству по Consumer
Холгер,
7
Мне нравится ответ Стюарта Маркса. И я думаю, что большинство людей, ответивших ниже, упустили суть. Вопрос не в том, «как» писать «Поставщики», «Потребители» и «Функции». Это «зачем» вы бы хотели? Для человека, который к ним не привык, они значительно усложняют код. Но польза от их использования не ясна.
anton1980
Насколько я могу судить (и я разделяю ваше разочарование касательными описаниями), это просто отличный способ абстрагироваться как от объектного типа, так и от объектной обработки от объекта, используемого в фрагменте кода. Это позволяет применять один и тот же код ко многим различным типам объектов, просто определяя разные новые классы и внедряя их в интерфейсы поставщика и потребителя. Таким образом, в системе учета полиции один и тот же поверхностный код используется для всех подозреваемых, но окончательная распечатка для каждого зависит от классификации каждого подозреваемого, например, «гражданин», «мелкий», «воровство», «преступник», «закоренелый», и т. д.
Trunk

Ответы:

96

Это поставщик:

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

Это Потребитель:

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

Итак, с точки зрения непрофессионала, поставщик - это метод, который возвращает какое-то значение (например, возвращаемое значение). Принимая во внимание, что потребитель - это метод, который потребляет некоторое значение (как в аргументе метода) и выполняет с ним некоторые операции.

Они превратятся во что-то вроде этого:

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

Что касается использования, самым простым примером будет Stream#forEach(Consumer)метод. Он принимает Consumer, который потребляет элемент из потока, в котором вы выполняете итерацию, и выполняет некоторые действия с каждым из них. Наверное, распечатайте.

Consumer<String> stringConsumer = (s) -> System.out.println(s.length());
Arrays.asList("ab", "abc", "a", "abcd").stream().forEach(stringConsumer);
Рохит Джайн
источник
3
Итак, поставщик - это способ создать экземпляр метода, который возвращает «что-то»?
Джеймс Эманон
3
@jamesemanon Совершенно верно. Это может быть ссылка на метод или лямбда.
Рохит Джайн,
15
В чем преимущество этого вместо прямого вызова метода? Причина в том, что поставщик может действовать как посредник и передавать это «возвращаемое» значение?
Джеймс Эманон
1
Consumer <Integer, Integer> недействителен. У потребителя есть параметр единственного типа.
nascar
2
Но ЗАЧЕМ создавать такую ​​конструкцию? Какую проблему решает наличие на Java?
Trunk
179

Причина, по которой вам трудно понять значение функциональных интерфейсов, таких как те, что в java.util.functionтом, что интерфейсы, определенные здесь, не имеют никакого значения! Они представлены в первую очередь для представления структуры , а не семантики .

Это нетипично для большинства API Java. Типичный Java API, такой как класс или интерфейс, имеет значение, и вы можете разработать мысленную модель того, что он представляет, и использовать ее для понимания операций с ней. Рассмотрим java.util.Listдля примера. A List- это контейнер других объектов. У них есть последовательность и индекс. Количество объектов, содержащихся в списке, возвращается size(). Каждый объект имеет индекс в диапазоне 0..размер-1 (включительно). Объект с индексом i можно получить, позвонив list.get(i). И так далее.

Функциональные интерфейсы java.util.functionне имеют такого значения. Вместо этого это интерфейсы, которые просто представляют структуру функции, такую ​​как количество аргументов, количество возвращаемых значений и (иногда) то, является ли аргумент или возвращаемое значение примитивом. Таким образом , мы имеем нечто подобное , Function<T,R>которое представляет собой функцию , которая принимает один аргумент типа T и возвращает значение типа R . Вот и все. Что делает эта функция? Что ж, он может делать что угодно ... до тех пор, пока он принимает единственный аргумент и возвращает единственное значение. Вот почему спецификация для этого Function<T,R>немного больше, чем «Представляет функцию, которая принимает один аргумент и производит результат».

Ясно, что когда мы пишем код, он имеет значение, и это значение должно откуда-то исходить. В случае функциональных интерфейсов значение исходит из контекста, в котором они используются. Интерфейс Function<T,R>не имеет смысла изолированно. Однако в java.util.Map<K,V>API есть следующее:

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

(символы подстановки опущены для краткости)

Ах, это использование Functionв качестве «функции отображения». Что это значит? В этом контексте, если он keyеще не присутствует на карте, вызывается функция сопоставления, и ей передается ключ, и ожидается, что она выдаст значение, а полученная пара ключ-значение вставляется в карту.

Таким образом, вы не можете смотреть на спецификацию Function(или любой другой функциональный интерфейс, если на то пошло) и пытаться понять, что они означают. Вы должны посмотреть, где они используются в других API, чтобы понять, что они означают, и это значение применимо только к этому контексту.

Стюарт Маркс
источник
4
По сути, это просто функция типа
Джек Гуо
Еще одна полезная информация: функциональные интерфейсы могут иметь несколько реализованных методов, которые могут добавлять поведение к вашему коду
Джон Марио Лотеро,
28

A Supplier- это любой метод, который не принимает аргументов и возвращает значение. Его работа буквально заключается в предоставлении экземпляра ожидаемого класса. Например, каждая ссылка на метод получения - этоSupplier

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

Ссылка на его метод экземпляра myClass::getCountявляется экземпляром Supplier<Integer>.

A Consumer- это любой метод, который принимает аргументы и ничего не возвращает. Его вызывают из-за его побочных эффектов. В терминах Java a Consumer- это идиома для voidметода. Хорошим примером являются методы setter:

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

Ссылка на его метод экземпляра myClass::setCountявляется экземпляром Consumer<Integer>и IntConsumer.

A Function<A,B>- это любой метод, который принимает аргумент одного типа и возвращает другой. Это можно назвать «трансформацией». Function<A,B>Принимает Aи возвращает B. Примечательно то, что для заданного значения Aфункция всегда должна возвращать конкретное значение B. Aи Bфактически может быть одного типа, например следующего:

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

Ссылка на его метод экземпляра myClass:addTwo- это a Function<Integer, Integer>и a ToIntFunction<Integer>.

Ссылка метода класса на получатель - еще один пример функции.

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

Ссылка на его метод класса MyClass::getCountявляется экземпляром Function<MyClass,Integer>и ToIntFunction<MyClass>.

Стив К
источник
15

Почему в пакете java.util.function определены интерфейсы потребителя / поставщика / других функциональных интерфейсов : Потребитель и поставщик - это два, среди многих, встроенных функциональных интерфейсов, представленных в Java 8. Назначение всех этих встроенных функциональных интерфейсов - предоставить готовый «шаблон» для функциональных интерфейсов, имеющих общие дескрипторы функций (сигнатуры / определения функциональных методов).

Допустим, у нас есть требование для преобразования типа T в другой тип R. Если бы мы должны были передать любую функцию, определенную таким образом, как параметр методу, тогда этот метод должен был бы определить функциональный интерфейс, функциональный / абстрактный метод которого принимает параметр типа T в качестве входа и дает параметр типа R в качестве выхода. Таких сценариев может быть много, и программист (ы) в конечном итоге определит несколько функциональных интерфейсов для своих нужд. Чтобы избежать такого сценария, упростить программирование и ввести общий стандарт в использовании функциональных интерфейсов, был определен набор встроенных функциональных интерфейсов, таких как Predicate, Function, Consumer & Supplier.

Что делает потребитель : функциональный интерфейс потребителя принимает ввод, что-то делает с этим вводом и не выдает никакого вывода. Его определение выглядит так (из источника Java) -

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

Здесь accept () - это функциональный \ абстрактный метод, который принимает входные данные и не возвращает выходных данных. Итак, если вы хотите ввести целое число, сделайте с ним что-нибудь без вывода, тогда вместо определения собственного интерфейса используйте экземпляр Consumer.

Что делает поставщик : функциональный интерфейс поставщика не принимает никаких входных данных, но возвращает выходные данные. Его определяют так (из источника Java) -

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

Везде, где вам нужна функция, которая что-то возвращает, скажем целое число, но не принимает никаких выходных данных, используйте экземпляр Supplier.

В случае необходимости большей ясности, наряду с примером использования интерфейсов потребителей и поставщиков, вы можете ссылаться на мои сообщения в блоге на том же самом - http://www.javabrahman.com/java-8/java-8-java-util- function-consumer-tutorial-with-examples / и http://www.javabrahman.com/java-8/java-8-java-util-function-supplier-tutorial-with-examples/

Дхрув Рай Пури
источник
12

1. Значение

См. Мои ответы на мой вопрос здесь, а также еще один здесь , но вкратце эти новые интерфейсы обеспечивают условность и наглядность для всех, чтобы их мог использовать (+ цепочка фанковых методов, например.forEach(someMethod().andThen(otherMethod()))

2. Различия

Потребитель : что-то берет, что-то делает, ничего не возвращает:void accept(T t)

Поставщик: ничего не берет, что-то возвращает: T get()(противоположность Consumer, в основном универсальный метод получения)

3. Использование

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

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

Поставщик: переносить повторяющийся код, например, время выполнения кода

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

С точки зрения непрофессионалов,

поставщик предоставит данные, но без использования каких-либо данных. В терминах программирования это метод, который не принимает никаких аргументов, но возвращает значение. Он используется для генерации новых значений.

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

потребитель будет потреблять данные и не возвращать никаких данных. В терминах программирования это метод, который принимает несколько аргументов и не возвращает никакого значения.

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

лалитбхагтани
источник
0

Потребитель и поставщик - это интерфейсы, предоставляемые java. Потребитель используется для перебора элементов списка, а поставщик - для объекта снабжения.

это легко понять с помощью демонстрации кода.

Потребитель

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

    }

}

Поставщик

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

Самый простой ответ может быть таким:

Потребителя можно рассматривать как функцию <T, Void>. Поставщика можно рассматривать как функцию <Void, T>.

Яшвин Мунсадвала
источник