我在这篇文章中遇到了一些聪明的代码,用于将Iterator转换为来自Karol的Stream .我必须承认,我不完全理解如何Iterable
在以下代码中将lambda分配给该类型...
staticStream iteratorToFiniteStream(final Iterator iterator) { final Iterable iterable = () -> iterator; return StreamSupport.stream(iterable.spliterator(), false); }
我决定编写自己的小测试以确保它编译和执行,并且确实如此.
public void printsStream_givenIterator() { Iteratoriterator = 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
充当供应商的职能.
Iterable不是FunctionalInterface,那么如何为它分配这个lambda?
() -> iterator
是不是 "作为一个Supplier
功能".可以将Lambda表达式分配给任何一致的功能接口类型.并且不需要对功能接口进行注释@FunctionalInterface
.您可以Iterable
使用lambda表达式创建一个lambda表达式,该表达式将实现该Iterable.iterator()
方法,该abstract
方法是该接口的唯一方法.但是,通过Iterator
每次返回相同的实例来实现它可能违反了能够多次迭代该对象的期望(这就是为什么Stream
Iterable
尽管有一个iterator()
方法仍然没有实现).
这个解决方案可以在这个狭窄的环境中工作,但并不聪明.
序列
final Iterableiterable = () -> iterator; … iterable.spliterator()
刚才介绍了一个额外的步骤Spliterators.spliteratorUnknownSize(iterator, 0)
,即实现该default
方法的方法Iterable.spliterator()
.
所以
staticStream iteratorToFiniteStream(final Iterator iterator) { final Iterable iterable = () -> iterator; return StreamSupport.stream(iterable.spliterator(), false); }
只是一个效率较低的变种
staticStream iteratorToFiniteStream(final Iterator iterator) { return StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, 0), false); }
如果将此与您已链接的问答答的已接受答案进行比较,您会发现它正是如此,但该答案将有机会传递特定characteristics
而不是0
.
正如您所指出的,从lambda表达式赋值仅在目标是功能接口时才有效.这在JLS的第15.27.3节中描述:Lambda表达式的类型.
如果T是函数接口类型(第9.8节),并且表达式与从T派生的地面目标类型的函数类型一致,则lambda表达式在赋值上下文,调用上下文或具有目标类型T的强制转换上下文中是兼容的.
跳转到第9.8节:功能接口,我们可以看到功能接口的定义.
功能接口是一个只有一个抽象方法(除了Object的方法)的接口,因此代表一个函数契约.
Iterable
确实满足功能接口的标准,因为它只有一个抽象方法:iterator()
.(另外default
两种方法不违反标准,因为它们不是抽象的.)因此,赋值是有效的.