【问题标题】:When and why would you use Java's Supplier and Consumer interfaces?您何时以及为什么会使用 Java 的供应商和消费者接口?
【发布时间】:2015-04-09 15:14:04
【问题描述】:

作为一名学习 Java 的非 Java 程序员,我目前正在阅读有关 SupplierConsumer 接口的信息。我无法理解它们的用法和含义。

您何时以及为何使用这些接口?谁能给我一个简单的外行例子吗?

我发现文档示例不够简洁,无法理解。

【问题讨论】:

  • API Doc 的每一页顶部都有一个标有“USE”的链接,您可以单击该链接找到ConsumerSupplier 您也可以在tutorial 中搜索Consumer...
  • 我喜欢 Stuart Marks 的回答。而且我认为下面回答的大多数人都没有抓住重点。问题不在于“如何”编写供应商、消费者和函数。它是“为什么”在世界上你想要的?对于不习惯它们的人来说,它们使代码变得更加复杂。但使用它们的好处尚不清楚。
  • 据我所见(我也分享您对切线描述的挫败感),这只是从一段代码中使用的对象抽象对象类型和对象处理的一种巧妙方法。这允许通过简单地定义不同的新类并将它们注入到供应商和消费者接口中,将相同的代码应用于许多不同类型的对象。因此,在警察记录系统中,所有嫌疑人都使用相同的表面代码,但每个嫌疑人的最终打印输出取决于每个嫌疑人的分类,例如'citizen'、'petty'、'larcen'、'felon'、'hardened'等

标签: java java-8


【解决方案1】:

这是供应商:

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

【讨论】:

  • 那么,Supplier 是一种创建返回“某物”的方法实例的方法?
  • @jamesemanon 没错。这可能是一个方法引用,或者一个 lambda。
  • 这样比直接调用方法有什么好处呢?是因为供应商可以充当中间人并传递“回报”价值吗?
  • Consumer 无效。消费者只有一个类型参数。
  • 但是为什么要创建这样一个结构呢?用Java解决了什么问题?
【解决方案2】:

Supplier 是任何不带参数并返回值的方法。它的工作实际上是提供预期类的实例。例如,对“getter”方法的每个引用都是Supplier

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

它的实例方法引用myClass::getCountSupplier&lt;Integer&gt;的一个实例。

Consumer 是任何接受参数但不返回任何内容的方法。调用它是因为它的副作用。在 Java 术语中,Consumervoid 方法的惯用语。 'setter' 方法就是一个很好的例子:

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

它的实例方法引用myClass::setCountConsumer&lt;Integer&gt;IntConsumer的一个实例。

Function&lt;A,B&gt; 是接受一种类型的参数并返回另一种类型的任何方法。这可以称为“转换”。 Function&lt;A,B&gt; 接受 A 并返回 B。值得注意的是,对于给定的A 值,该函数应始终返回特定值BAB 其实可以是同一个类型,比如:

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

它的实例方法引用myClass:addTwo是一个Function&lt;Integer, Integer&gt;和一个ToIntFunction&lt;Integer&gt;

对 getter 的 Class 方法引用是函数的另一个示例。

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

它的类方法引用MyClass::getCountFunction&lt;MyClass,Integer&gt;ToIntFunction&lt;MyClass&gt;的一个实例。

【讨论】:

    【解决方案3】:

    为什么在 java.util.function 包中定义 Consumer/Supplier/other 功能接口:Consumer 和 Supplier 是 Java 8 中提供的众多内置功能接口中的两个。所有这些内置功能接口的目的是为具有通用功能描述符(功能方法签名/定义)的功能接口提供现成的“模板”。

    假设我们需要将类型 T 转换为另一种类型 R。如果我们要将 any 像这样定义的函数作为参数传递给方法,那么该方法需要定义一个功能接口,其功能/抽象方法将 T 类型的参数作为输入,并将 R 类型的参数作为输出。现在,可能有很多这样的场景,程序员最终会根据他们的需要定义多个功能接口。为了避免这种情况,简化编程并为函数式接口的使用带来一个通用标准,我们定义了一组内置的函数式接口,例如 Predicate、Function、Consumer 和 Supplier。

    Consumer 做什么:Consumer 功能接口接受输入,对输入执行某些操作,但不提供任何输出。它的定义是这样的(来自Java Source)——

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

    这里的 accept() 是函数式\抽象方法,它接受输入但不返回输出。所以,如果你想输入一个整数,用它做一些没有输出的事情,然后不要定义你自己的接口,而是使用消费者的实例。

    Supplier 做什么:Supplier 功能接口不接受任何输入,而是返回一个输出。它的定义是这样的(来自Java Source) -

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

    只要你需要一个函数来返回一些东西,比如一个整数,但不接受输出,请使用供应商的实例。

    如果需要更清晰的消费者和供应商接口以及示例用法,那么您可以参考我的博客文章 - 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/

    【讨论】:

      【解决方案4】:

      消费者和供应商是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());
          }
      
      }
      

      【讨论】:

        【解决方案5】:

        1。意义

        查看我对我的问题here 和另一个here 的回答,但简而言之,这些新接口提供约定描述性供所有人使用(+ funky方法链如.forEach(someMethod().andThen(otherMethod()))

        2。差异

        消费者:接受某事,做某事,不返回任何内容:void accept(T t)

        供应商:什么都不拿,返回什么:T get()(消费者的反面,基本上是一种通用的“getter”方法)

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

        【讨论】:

          【解决方案6】:

          用外行的话,

          供应商将提供数据但不消耗任何数据。在编程术语中,一种不接受任何参数但返回值的方法。它用于生成新值。

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

          consumer 将消费数据但不返回任何数据。在编程术语中,一个方法接受多个参数并且不返回任何值。

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

          【讨论】:

            【解决方案7】:

            你很难理解java.util.function中的函数接口的含义是因为这里定义的接口没有任何意义!它们主要用于表示结构,而不是语义

            这对于大多数 Java API 来说是非典型的。典型的 Java API(例如类或接口)是有意义的,您可以为它所代表的内容开发一个心智模型,并使用它来理解对它的操作。以java.util.List 为例。 List 是其他对象的容器。它们有一个序列和一个索引。列表中包含的对象数由size() 返回。每个对象都有一个范围为 0..size-1(含)的索引。可以通过调用list.get(i) 来检索索引i 处的对象。以此类推。

            java.util.function 中的功能接口没有这个含义。相反,它们是仅表示函数的结构 的接口,例如参数的数量、返回值的数量,以及(有时)参数或返回值是否是原始的。因此,我们有类似Function&lt;T,R&gt; 的东西,它表示一个函数,它接受一个T 类型的参数并返回一个R 类型的值。而已。那个函数有什么作用?好吧,它可以做任何事情……只要它接受一个参数并返回一个值。这就是为什么Function&lt;T,R&gt; 的规范只不过是“表示一个接受一个参数并产生结果的函数。”

            显然,当我们编写代码时,它具有意义,而这种意义必须来自某个地方。在功能接口的情况下,含义来自使用它们的上下文。接口Function&lt;T,R&gt; 孤立起来没有意义。但是,在java.util.Map&lt;K,V&gt; API 中,有以下内容:

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

            (为简洁起见省略了通配符)

            啊,Function 的这种用法是作为“映射函数”。那有什么作用?在这种情况下,如果 key 尚未出现在映射中,则调用映射函数并将键传递给预期并生成一个值,然后将生成的键值对插入映射中。

            因此,您无法查看Function(或任何其他功能接口,就此而言)的规范并尝试辨别它们的含义。您必须查看它们在其他 API 中的使用位置才能理解它们的含义,而该含义仅适用于该上下文。

            【讨论】:

            • 所以基本上,它只是作为类型起作用
            • 另一个有用的信息可能是函数式接口可以有多个实现的方法,可以为你的代码添加行为
            • 最后两段为我解决了这个谜团(或者至少是剩下的部分)。当我尝试使用 forge 的 API 编写(或学习编写)Minecraft mod 时,我第一次遇到了供应商。我正在遵循的演练显示编写一个函数,该函数接收名为“块”的参数的供应商,根据您所说的,这可能是因为块可以根据其放置表提供不同的东西(尽管通常它会自行掉落)。这是这里的总体思路吗?一个类,根据上下文,可以“获取”(或在消费者的情况下设置)不同的东西?
            猜你喜欢
            • 2018-05-02
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2020-02-11
            • 2010-10-05
            • 1970-01-01
            • 2012-12-24
            相关资源
            最近更新 更多