21

我正在流式传输实现接口的类的对象。我想将它们收集为接口元素的列表,而不是实现类。

这对于 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 来收集 Java 16 中的类接口列表?

如果有人有比明确铸造更好的解决方案,我也很乐意听到:)

4

3 回答 3

21

.collect(Collectors.toList())有效,因为签名collect是:

<R, A> R collect(Collector<? super T, A, R> collector);

重要的部分是? super T

这意味着toList()收集器可以解释为Collector<Dodo,?,List<Dodo>(当您将结果分配.collect()给 a时List<Dodo>),即使您的流的类型是Stream<FancyDodo>.

另一方面,Stream's的签名toList()是:

List<T> toList()

所以如果你为 a 执行它Stream<FancyDodo>,你会得到 a List<FancyDodo>,它不能被分配给一个List<Dodo>变量。

我建议您在这种情况下简单地使用stream.collect(Collectors.toList())而不是。stream.toList()

于 2021-05-13T10:10:00.817 回答
18

因为Stream.toList被声明返回一个List<T>

default List<T> toList() { ... }

其中T是流的元素类型。我真的想不出另一种声明方式,toList以便它可以返回您想要的列表类型。您能做的最好的事情就是接受 aList<? super T>作为参数,并向其中添加流元素,但这违背了流的“美学”——这一切的重点是声明性的并且几乎没有状态。

您可以重写代码toList返回所需类型列表的一种方法是T手动指定类型。现在T推断是FancyDodo由于,但如果你愿意,你可以Stream.of(new FancyDodo())强制:TDodo

Stream.<Dodo>of(new FancyDodo()).toList();

现在TDodotoList将返回一个List<Dodo>


您能做的最好的事情是接受 aList<? super T>作为参数,并将流元素添加到其中

实际上,这就是Collector正在做的事情。注意如何collect接受 aCollector<? super T, DoesntMatter, R>并返回R。该逆变式? super T使您能够使用这样的toList收集器。另请注意,这R是 的通用参数collect,这意味着您可以决定collect返回什么,只要您可以提供收集到 的收集? super TR

于 2021-05-13T10:14:23.007 回答
8

泛型类型参数解析一次发生一个方法调用。

Stream.of(new FancyDodo())始终解析TFancyDodo,因此将始终导致 a Stream<FancyDodo>

toList()不解决T,它只是使用已经建立的T,所以结果总是 List<FancyDodo>,并且List<FancyDodo>不兼容List<Dodo>。请参阅:“List<Dog>子类List<Animal>吗?为什么 Java 泛型不是隐式多态的?

collect(Collectors.toList())有不同TCollectors.toList(),可以解决不同TStream。编译器将其解析TDodo,因为所需的返回类型为List<Dodo>

于 2021-05-13T10:09:13.263 回答