Explicação do fornecedor e consumidor do Java 8 para leigos


99

Como um programador não Java que está aprendendo Java, estou lendo sobre interfaces Suppliere Consumerno momento. E não consigo entender seu uso e significado. Quando e por que você usaria essas interfaces? Alguém pode me dar um exemplo simples de um leigo disso ... Estou achando os exemplos do Doc não sucintos o suficiente para o meu entendimento.


4
Cada página do API Doc tem um link rotulado “USE” na parte superior que você pode clicar Consumere Suppliertambém pode pesquisar o tutorial para Consumer...
Holger

7
Adoro a resposta de Stuart Marks. E eu acho que a maioria das pessoas que responderam abaixo não entendeu o ponto. A questão não é "como" escrever Fornecedores, Consumidores e Funções. É "por que" no mundo você gostaria? Para uma pessoa que não está acostumada com eles, eles tornam o código muito mais complexo. Mas o benefício de usá-los não é claro.
anton1980

Pelo que posso ver (e compartilho sua frustração com as descrições tangenciais), é apenas uma maneira engenhosa de abstrair tanto o tipo de objeto quanto o tratamento de objeto de um objeto usado em um trecho de código. Isso permite a aplicação desse mesmo código a muitos tipos diferentes de objeto, simplesmente definindo novas classes diferentes e injetando-as nas interfaces Fornecedor e Consumidor. Portanto, em um sistema de registro policial, o mesmo código superficial é usado para todos os suspeitos, mas a impressão final para cada um depende da classificação de cada suspeito, por exemplo, 'cidadão', 'mesquinho', 'furto', 'criminoso', 'endurecido', etc.
Tronco de

Respostas:


95

Este é o fornecedor:

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

Este é o consumidor:

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

Portanto, em termos leigos, um fornecedor é um método que retorna algum valor (como em seu valor de retorno). Considerando que, um consumidor é um método que consome algum valor (como no argumento do método) e executa algumas operações sobre eles.

Eles se transformarão em algo assim:

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

Quanto ao uso, o exemplo básico seria: Stream#forEach(Consumer)método. É necessário um Consumidor, que consome o elemento do fluxo no qual você está iterando, e executa alguma ação em cada um deles. Provavelmente imprimi-los.

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

3
Portanto, um fornecedor é uma maneira de criar uma instância de um método que retorna 'algo'?
James emanon

3
@jamesemanon Exatamente. Isso pode ser uma referência de método ou um lambda.
Rohit Jain

14
Qual é a vantagem disso, em vez de chamar o método diretamente? É porque o fornecedor pode agir como um intermediário e entregar esse valor de "retorno"?
James emanon

1
Consumidor <Integer, Integer> não é válido. Um consumidor possui um único parâmetro de tipo.
nascar

2
Mas POR QUE criar tal construção? Que problema é resolvido tendo-o em Java?
Tronco de

175

A razão pela qual você está tendo dificuldade em entender o significado de interfaces funcionais como aquelas em java.util.functioné que as interfaces definidas aqui não têm nenhum significado! Eles estão presentes principalmente para representar a estrutura , não a semântica .

Isso é atípico para a maioria das APIs Java. A API Java típica, como uma classe ou interface, tem significado, e você pode desenvolver um modelo mental para o que ela representa e usar isso para entender as operações nela. Considere, java.util.Listpor exemplo. A Listé um contêiner de outros objetos. Eles têm uma sequência e um índice. O número de objetos contidos na lista é retornado por size(). Cada objeto possui um índice no intervalo 0..size-1 (inclusive). O objeto no índice i pode ser recuperado chamando list.get(i). E assim por diante.

As interfaces funcionais em java.util.functionnão têm esse significado. Em vez disso, são interfaces que meramente representam a estrutura de uma função, como o número de argumentos, o número de valores de retorno e (às vezes) se um argumento ou valor de retorno é um primitivo. Assim, temos algo como Function<T,R>o que representa uma função que leva um único argumento do tipo T e retorna um valor do tipo R . É isso aí. O que essa função faz? Bem, ele pode fazer qualquer coisa ... desde que tenha um único argumento e retorne um único valor. É por isso que a especificação para Function<T,R>é pouco mais do que "Representa uma função que aceita um argumento e produz um resultado."

Claramente, quando estamos escrevendo um código, ele tem um significado, e esse significado deve vir de algum lugar. No caso das interfaces funcionais, o significado vem do contexto em que são usadas. A interface Function<T,R>não tem significado isoladamente. No entanto, na java.util.Map<K,V>API, existe o seguinte:

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

(curingas elididos por brevidade)

Ah, esse uso de Functioné uma "função de mapeamento". O que isso faz? Nesse contexto, se keyainda não estiver presente no mapa, a função de mapeamento é chamada e recebe a chave e espera-se que produza um valor, e o par chave-valor resultante é inserido no mapa.

Portanto, você não pode olhar para a especificação Function(ou qualquer uma das outras interfaces funcionais, por falar nisso) e tentar discernir o que elas significam. Você precisa ver onde eles são usados ​​em outras APIs para entender o que significam, e esse significado se aplica apenas a esse contexto.


3
Então, basicamente, é apenas funcionar como tipo
JGuo

Outra informação útil pode ser que as interfaces funcionais podem ter vários métodos implementados que podem adicionar comportamento ao seu código
Jhon Mario Lotero

28

A Supplieré qualquer método que não aceita argumentos e retorna um valor. Seu trabalho é literalmente fornecer uma instância de uma classe esperada. Por exemplo, toda referência a um método 'getter' é umSupplier

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

Sua referência de método de instância myClass::getCounté uma instância de Supplier<Integer>.

A Consumeré qualquer método que aceita argumentos e não retorna nada. É invocado por seus efeitos colaterais. Em termos de Java, a Consumeré uma expressão idiomática para um voidmétodo. Os métodos 'setter' são um bom exemplo:

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

Sua referência de método de instância myClass::setCounté uma instância de Consumer<Integer>e IntConsumer.

A Function<A,B>é qualquer método que recebe um argumento de um tipo e retorna outro. Isso pode ser referido como uma 'transformação'. O Function<A,B>recebe um Ae retorna um B. É notável que, para um determinado valor de A, a função sempre deve retornar um valor específico de B. Ae Bpode, na verdade, ser do mesmo tipo, como o seguinte:

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

Sua referência de método de instância myClass:addTwoé ae Function<Integer, Integer>a ToIntFunction<Integer>.

Uma referência ao método Class para um getter é outro exemplo de uma função.

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

Sua referência de método de classe MyClass::getCounté uma instância de Function<MyClass,Integer>e ToIntFunction<MyClass>.


15

Por que o Consumidor / Fornecedor / outras interfaces funcionais são definidas no pacote java.util.function : Consumidor e Fornecedor são duas, entre muitas, das interfaces funcionais integradas fornecidas em Java 8. O objetivo de todas essas interfaces funcionais integradas é para fornecer um "modelo" pronto para interfaces funcionais com descritores de função comuns (assinaturas / definições de métodos funcionais).

Digamos que temos um requisito para converter um tipo T em outro tipo R. Se passássemos qualquer função definida assim como um parâmetro para um método, então esse método precisaria definir uma Interface Funcional cujo método funcional / abstrato receba parâmetro do tipo T como entrada e fornece um parâmetro do tipo R como saída. Agora, poderia haver muitos cenários como este e o (s) programador (es) acabariam definindo várias interfaces funcionais para suas necessidades. Para evitar esse tipo de cenário, facilite a programação e traga um padrão comum no uso de interfaces funcionais, um conjunto de interfaces funcionais embutidas como Predicado, Função, Consumidor e Fornecedor foram definidas.

O que o consumidor faz : a interface funcional do consumidor aceita uma entrada, faz algo com essa entrada e não fornece nenhuma saída. Sua definição é assim (da fonte Java) -

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

Aqui, accept () é o método funcional \ abstrato que recebe uma entrada e não retorna nenhuma saída. Portanto, se você quiser inserir um Inteiro, faça algo com ele sem saída e, em vez de definir sua própria interface, use uma instância de Consumidor.

O que o fornecedor faz : a interface funcional do fornecedor não recebe nenhuma entrada, mas retorna uma saída. É definido assim (da fonte Java) -

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

Sempre que você precisar de uma função que retorna algo, digamos um inteiro, mas não leva nenhuma saída, use uma instância de Fornecedor.

Caso seja necessária mais clareza, junto com o uso de exemplo, das interfaces de consumidor e fornecedor, você pode consultar minhas postagens de blog sobre as mesmas - 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. Significado

Veja minhas respostas à minha pergunta aqui e também a outra aqui , mas em resumo essas novas interfaces fornecem convenção e descritividade para que todos possam usar (+ encadeamento de métodos funky como.forEach(someMethod().andThen(otherMethod()))

2. Diferenças

Consumidor : pega algo, faz algo, não retorna nada:void accept(T t)

Fornecedor: não leva nada, retorna algo: T get()(reverso do Consumidor, basicamente um método 'getter' universal)

3. Uso

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

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

Fornecedor: embrulhe código repetitivo, por exemplo, tempo de execução de código

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

Consumidor e fornecedor são as interfaces fornecidas por java. O consumidor é usado para iterar sobre os elementos da lista e o fornecedor é usado para o objeto de fornecimento

você pode entender facilmente com demonstração de código.

Consumidor

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

    }

}

Fornecedor

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

A resposta mais simples pode ser:

Um consumidor pode ser visto como uma função <T, vazio>. Um Fornecedor pode ser visto como uma Função <Vazio, T>.


0

Em resumo,

  1. Fornecedor

representa uma função anônima que não aceita nenhum argumento e produz um resultado.

  1. Consumidor

representa uma função anônima que aceita um argumento e não produz nenhum resultado.

Além disso, existe um predicado . Predicado representa uma função anônima que aceita um argumento e produz um resultado.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.