解释如何将lambda分配给Iterable

时间:2017-01-25 00:18:23

标签: java lambda java-8

我遇到了一些聪明的代码,可以在this post上将Iterator转换为来自Karol的Stream。我必须承认,我并不完全理解如何在以下代码中将lambda分配给Iterable类型...

static <T> Stream<T> iteratorToFiniteStream(final Iterator<T> iterator) {
    final Iterable<T> iterable = () -> iterator;
    return StreamSupport.stream(iterable.spliterator(), false);
}

我决定编写自己的小测试,以确保它编译和执行,并确实如此。

public void printsStream_givenIterator()
{
    Iterator<String> iterator = Arrays.asList("a", "b", "c").iterator();
    final Iterable<String> iterable = () -> iterator;
    StreamSupport.stream(iterable.spliterator(), false).forEach(s -> System.out.println(s));
}

// prints: abc

我的理解是lambda () -> iterator充当Supplier函数。

Iterable不是FunctionalInterface所以如何分配这个lambda?

2 个答案:

答案 0 :(得分:7)

() -> iterator “充当Supplier函数”。可以将Lambda表达式分配给任何一致的功能接口类型。并且不需要使用@FunctionalInterface注释功能接口。您可以使用lambda表达式创建Iterable,这将实现Iterable.iterator()方法,该方法是该接口的唯一abstract方法。但是,通过每次返回相同的Iterator实例来实现它可能违反了能够多次迭代此对象的期望(这是why Stream doesn’t implement Iterable尽管有iterator()方法的原因)。

这个解决方案可以在这个狭窄的环境中工作,但并不聪明。

序列

final Iterable<T> iterable = () -> iterator; … iterable.spliterator()

只是在Spliterators.spliteratorUnknownSize(iterator, 0)之前引入了一个额外的步骤,即default Iterable.spliterator()方法的实现方式。

所以

static <T> Stream<T> iteratorToFiniteStream(final Iterator<T> iterator) {
    final Iterable<T> iterable = () -> iterator;
    return StreamSupport.stream(iterable.spliterator(), false);
}

只是

效率较低的变种
static <T> Stream<T> iteratorToFiniteStream(final Iterator<T> iterator) {
    return StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, 0), false);
}

如果您将此与您已关联的问答答的已接受答案进行比较,您会发现确实如此,但该答案有机会传递特定characteristics而不是{{1 }}

答案 1 :(得分:5)

正如您所指出的,只有目标是功能接口时,从lambda表达式赋值才有效。这在section 15.27.3 of the JLS: Type of a Lambda Expression

中有所描述
  

如果T是函数接口类型(第9.8节)并且表达式与派生的地面目标类型的函数类型一致,则lambda表达式在赋值上下文,调用上下文或具有目标类型T的强制转换上下文中是兼容的来自T。

跳转到section 9.8: Functional Interfaces,我们可以看到功能界面的定义。

  

功能接口是一个只有一个抽象方法的接口(除了Object的方法),因此代表一个函数契约。

Iterable确实满足功能接口的标准,因为它只有一个抽象方法:iterator()。 (另外两个default方法不违反标准,因为它们不是抽象的。)因此,赋值是有效的。