我遇到了一些聪明的代码,可以在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?
答案 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
方法不违反标准,因为它们不是抽象的。)因此,赋值是有效的。