【问题标题】:java8 + internal working of for each loopjava8 +每个循环的内部工作
【发布时间】:2018-10-15 18:29:35
【问题描述】:

我是 java8 的新手。我试图了解 Streams 中 foreach 循环的工作原理。 我的代码如下:-

Stream<String> s = someist.stream();
Consumer<String> consumer = (String s1)->System.out.println(s1);
s.forEach(consumer);

问题是当我只是传递 Consumer 的引用而不是在其中调用 consumer.accept(T t) 时,foreach 循环如何知道调用 consumer.accept(T t)。虽然,当我们为函数式接口提供一些 lambda 表达式时,我们需要调用它的抽象方法,但我们只是没有在这里调用它。 那么 foreach 循环如何知道它应该如何处理作为参数提供的消费者引用呢?

【问题讨论】:

    标签: java foreach java-8 java-stream


    【解决方案1】:

    forEach的具体实现会调用accept,例如:

    public class MyStream implements Stream<String> {
    
        @Override
        public void forEach(Consumer<? super String> action) {
            while (hasNext()) {
                action.accept(next());
            }
        }
    
        ...
    }
    

    【讨论】:

    • 你有一些官方文档的链接吗?
    • @Jubobs,这个答案中的代码实现了Stream 接口。所以这是一个自定义实现。但是,forEach() 在其每个实现类中都有不同的实现(例如,(java.lang.Iterablejava.util.ArrayList 等)。您可以从其源代码中轻松阅读。
    • 这仍然没有回答原始问题 - 接口 Stream 的 forEach 方法不是默认方法 - 因此没有实现 - 它只是抽象的。那么当我们调用 Stream.forEach(Consumer c1) - 谁在调用 'accept(T t)' ?在 Iterable 中的“forEach”的情况下——我们确实看到了一个调用 Consumer.accept 的默认实现。
    【解决方案2】:

    正如 java 文档所述:

    void forEach​(Consumer&lt;? super T&gt; action)

    为每个动作执行一个动作 此流的元素。

    即传递给forEach 方法的行为参数将为源的每个元素在内部调用。

    【讨论】:

    • 感谢您的快速回复。但是“forEaach”的文档指出,我们只需要告诉需要执行的业务,我们不处理迭代机制。但是这里我们只是传递了参数,没有说明如何处理这个参数。以下代码很容易理解:-stream.forEach(new Consumer() { public void accept(String name) { System.out.println(name); } });但我无法理解仅通过将消费者引用作为参数传递来调用“接受”方法的机制。
    • @SuvigyaGupta 从 Java 1.0 开始,它与 Thread 调用指定 run() 方法的机制相同@ 987654326@。底层实现代码将调用该方法。目前尚不清楚“传递参数而不说明如何处理此参数”有什么问题。在将参数传递给方法时,您永远不会声明这一点。该方法的实现代码就是这样做的。
    • @Holger 所以你说的是 Stream.forEach 是 Java 的内部实现,我们不需要担心吗?我认为混淆是因为在 Java 8 中的 Iterable 类中我们也有一个默认的“forEach”方法,它有一个实现,这就是为什么我也想知道 Stream 中的“forEach”方法的实现在哪里。请确认这是否是正确的理解。 + 1 为您的上述 cmets。也请指出任何有关相同的文件?
    • @satishmarathe 是的,“别担心”是最好的结论,你可以做出。请记住,default 方法可以像任何其他接口方法一样被覆盖,因此Iterable 有一个带有default 实现的forEach 方法,但更多时候,实现(例如特定集合)已经用它覆盖了它一种更适合特定收藏的方法。同样,Stream.forEach 最终可能会得到完全不同的实现代码,具体取决于实际的源和链式操作(以及是顺序的还是并行的)。
    【解决方案3】:

    这就是Functional Interface 的工作原理。他们只有一种abstract 方法。可能有可选的default 方法。 abstract 方法的行为以lambda expression 的形式传递(传递行为就像数据一样)。这种行为又是Functional Interfaceabstract 方法的方法体。 default 方法使用它们的名称调用。

    所以当你将Functional Interface作为方法参数传递给另一个方法时,在这种情况下,Consumer&lt;T&gt;java.util.stream.Stream接口的forEach()方法,它的抽象方法accept(T t)被调用流。

    这样说,从逻辑上讲,如果仅将Functional Interface 作为参数传递,则只有一个方法可以调用。

    【讨论】:

      猜你喜欢
      • 2017-03-02
      • 2013-10-30
      • 2021-09-11
      • 1970-01-01
      • 2014-03-22
      • 1970-01-01
      • 2014-12-23
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多