为什么我不能使用 Stream#toList 在 Java 16 中收集 class' 接口的列表?
Why can't I use Stream#toList to collect a list of a class' interface in Java 16?
我正在流式传输 class 实现接口的对象。我想将它们收集为界面元素列表,而不是实现 class.
使用 Java 16.0.1 的 Stream#toList
方法似乎不可能。例如在下面的代码中,最后一条语句将无法编译。
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class WhyDodo {
private interface Dodo { }
private static class FancyDodo implements Dodo { }
public static void main(String[] args) {
final List<Dodo> dodos = Stream.of(new FancyDodo()).collect(Collectors.toList());
final List<FancyDodo> fancyDodos = Stream.of(new FancyDodo()).toList();
final List<Dodo> noFancyDodos = Stream.of(new FancyDodo()).toList();
}
}
我们可以明确地将每个元素从 FancyDodo
转换为 Dodo
。但至少为了简洁起见,我们也可以使用 .collect(Collectors.toList())
.
为什么我不能使用 Stream#toList 在 Java16 中收集 class' 接口的列表?
如果有人有比显式转换更好的解决方案,我也很乐意听到:)
泛型类型参数解析一次发生一个方法调用。
Stream.of(new FancyDodo())
将 总是 将 T
解析为 FancyDodo
,因此总是会导致 Stream<FancyDodo>
.
toList()
不解析T
,只是使用已经建立的T
,所以结果是alwaysList<FancyDodo>
,并且 List<FancyDodo>
与 List<Dodo>
不兼容。请参阅:“Is List<Dog>
a subclass of List<Animal>
? Why are Java generics not implicitly polymorphic?”
collect(Collectors.toList())
在 Collectors.toList()
中有一个不同的 T
,可以与 Stream
的 T
不同地解析。由于 List<Dodo>
.
所需的 return 类型,编译器将 T
解析为 Dodo
.collect(Collectors.toList())
有效,因为 collect
的签名是:
<R, A> R collect(Collector<? super T, A, R> collector);
重要的部分是 ? super T
这意味着 toList()
收集器可以解释为 Collector<Dodo,?,List<Dodo>
(当您将 .collect()
的结果分配给 List<Dodo>
时),即使您的流类型是 Stream<FancyDodo>
.
另一方面,Stream
的toList()
的签名是:
List<T> toList()
所以如果你为 Stream<FancyDodo>
执行它,你会得到一个 List<FancyDodo>
,它不能分配给 List<Dodo>
变量。
在这种情况下,我建议您只使用 stream.collect(Collectors.toList())
而不是 stream.toList()
。
因为Stream.toList
声明为return一个List<T>
:
default List<T> toList() { ... }
其中 T
是流的元素类型。我真的想不出另一种声明 toList
的方法,这样它就可以 return 您想要的列表类型。您能做的最好的事情就是接受一个 List<? super T>
作为 参数 ,并向其中添加流元素,但这种做法违背了流的“美学”——整个这一点是声明性的并且几乎没有状态。
您可以重写 您的代码 以使 toList
return 成为您所需类型的列表的一种方法是指定 T
手动。由于 Stream.of(new FancyDodo())
,现在 T
被推断为 FancyDodo
,但如果需要,您可以强制 T
为 Dodo
:
Stream.<Dodo>of(new FancyDodo()).toList();
现在 T
是 Dodo
,toList
将 return 变成 List<Dodo>
。
The best you can do is to accept a List<? super T>
as argument, and add the stream elements to it
实际上,这就是 Collector
正在做的事情。注意 collect
如何接受 Collector<? super T, DoesntMatter, R>
和 returns R
。逆变 ? super T
使您能够像那样使用 toList
收集器。另请注意,R
是 collect
的通用参数,这意味着 你可以决定 collect
returns,因为只要你能提供一个收集 ? super T
到 R
.
的收集器
我正在流式传输 class 实现接口的对象。我想将它们收集为界面元素列表,而不是实现 class.
使用 Java 16.0.1 的 Stream#toList
方法似乎不可能。例如在下面的代码中,最后一条语句将无法编译。
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class WhyDodo {
private interface Dodo { }
private static class FancyDodo implements Dodo { }
public static void main(String[] args) {
final List<Dodo> dodos = Stream.of(new FancyDodo()).collect(Collectors.toList());
final List<FancyDodo> fancyDodos = Stream.of(new FancyDodo()).toList();
final List<Dodo> noFancyDodos = Stream.of(new FancyDodo()).toList();
}
}
我们可以明确地将每个元素从 FancyDodo
转换为 Dodo
。但至少为了简洁起见,我们也可以使用 .collect(Collectors.toList())
.
为什么我不能使用 Stream#toList 在 Java16 中收集 class' 接口的列表?
如果有人有比显式转换更好的解决方案,我也很乐意听到:)
泛型类型参数解析一次发生一个方法调用。
Stream.of(new FancyDodo())
将 总是 将 T
解析为 FancyDodo
,因此总是会导致 Stream<FancyDodo>
.
toList()
不解析T
,只是使用已经建立的T
,所以结果是alwaysList<FancyDodo>
,并且 List<FancyDodo>
与 List<Dodo>
不兼容。请参阅:“Is List<Dog>
a subclass of List<Animal>
? Why are Java generics not implicitly polymorphic?”
collect(Collectors.toList())
在 Collectors.toList()
中有一个不同的 T
,可以与 Stream
的 T
不同地解析。由于 List<Dodo>
.
T
解析为 Dodo
.collect(Collectors.toList())
有效,因为 collect
的签名是:
<R, A> R collect(Collector<? super T, A, R> collector);
重要的部分是 ? super T
这意味着 toList()
收集器可以解释为 Collector<Dodo,?,List<Dodo>
(当您将 .collect()
的结果分配给 List<Dodo>
时),即使您的流类型是 Stream<FancyDodo>
.
另一方面,Stream
的toList()
的签名是:
List<T> toList()
所以如果你为 Stream<FancyDodo>
执行它,你会得到一个 List<FancyDodo>
,它不能分配给 List<Dodo>
变量。
在这种情况下,我建议您只使用 stream.collect(Collectors.toList())
而不是 stream.toList()
。
因为Stream.toList
声明为return一个List<T>
:
default List<T> toList() { ... }
其中 T
是流的元素类型。我真的想不出另一种声明 toList
的方法,这样它就可以 return 您想要的列表类型。您能做的最好的事情就是接受一个 List<? super T>
作为 参数 ,并向其中添加流元素,但这种做法违背了流的“美学”——整个这一点是声明性的并且几乎没有状态。
您可以重写 您的代码 以使 toList
return 成为您所需类型的列表的一种方法是指定 T
手动。由于 Stream.of(new FancyDodo())
,现在 T
被推断为 FancyDodo
,但如果需要,您可以强制 T
为 Dodo
:
Stream.<Dodo>of(new FancyDodo()).toList();
现在 T
是 Dodo
,toList
将 return 变成 List<Dodo>
。
The best you can do is to accept a
List<? super T>
as argument, and add the stream elements to it
实际上,这就是 Collector
正在做的事情。注意 collect
如何接受 Collector<? super T, DoesntMatter, R>
和 returns R
。逆变 ? super T
使您能够像那样使用 toList
收集器。另请注意,R
是 collect
的通用参数,这意味着 你可以决定 collect
returns,因为只要你能提供一个收集 ? super T
到 R
.