解释如何将lambda分配给Iterable

我在这篇文章中遇到了一些聪明的代码,用于将Iterator转换为来自Karol的Stream。 我必须承认,我并不完全理解如何在以下代码中将lambda分配给Iterable类型…

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

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

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

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

Iterable不是FunctionalInterface,那么如何为它分配这个lambda?

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

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

序列

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

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

所以

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

只是一种效率较低的变种

 static  Stream iteratorToFiniteStream(final Iterator iterator) { return StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, 0), false); } 

如果将此与您所链接的问答答的已接受答案进行比较,您会发现它正是如此,但该答案有机会传递特定的characteristics而不是0

正如您所指出的,从lambda表达式赋值仅在目标是function接口时才有效。 这在JLS的第15.27.3节中描述:Lambda表达式的类型 。

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

跳转到第9.8节:function接口 ,我们可以看到function接口的定义。

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

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