Collectors.toList 和 Stream.toList 模板参数区别

sna*_*ran 1 java java-16

发现collect(Collectors.toList())和Stream.toList()之间的区别。看

class Animal { }
class Cat extends Animal { }
record House(Cat cat) { }

class Stuff {
    public static void function() {
        List<House> houses = new ArrayList<>();
        List<Animal> animals1 = 
            houses.stream()
                  .map(House::cat)
                  .collect(Collectors.toList()); // ok
        List<Animal> animals2 =
            houses.stream()
                  .map(House::cat).toList(); // compile error
        List<Animal> animals3 =
            houses.stream()
                  .map(House::cat)
                  .map(cat -> (Animal) cat).toList(); // ok
    }
}
Run Code Online (Sandbox Code Playgroud)

collect(Collectors.toList()) 能够给我一个动物列表或猫列表。但是Stream.toList()只能给出Cat的List。

问题是有什么方法可以让 Stream.toList() 工作。在我的现实世界示例中,我有一个类覆盖 shutdownNow,它返回 Runnable 列表,因此我的类调用了 some.stream().collect(Collectors.toList()),但是 some.stream().toList()返回 MyRunnable 的列表。

我的一部分希望他们将函数声明为而default <U super T> List<U> toList()不是default List<T> toList(),尽管奇怪的是,这在我的机器上是一个编译错误(我的编译器似乎可以使用 U extends T,而不是 U super T)。

Bri*_*etz 7

这里有一个简单的答案。

从...开始

var stream = houses.stream().map(House::cat)
Run Code Online (Sandbox Code Playgroud)

这里,stream有类型Stream<Cat>。该Stream::toList方法为您提供流元素类型的列表,此处为Catstream.toList()类型也是如此List<Cat>。这里别无选择。

Collector有多个类型变量,包括输入元素的类型和输出结果的类型。Collector创建接受Cat并生成List<Cat>List<Animal>、等的 具有很大的灵活性。Set<Animal>这种灵活性部分地被 的通用性Stream::collect(以及通用方法Collectors::toList)所隐藏;推断此方法的泛型类型参数可以考虑 LHS 上所需的结果类型。因此,语言为您掩盖了Cat和之间的差距Animal,因为流和结果之间存在另一层间接性。

正如@Eugene 指出的,你可以得到一个更通用的类型:

List<? extends Animal> animals2 = houses.stream().map(House::cat).toList()
Run Code Online (Sandbox Code Playgroud)

这与流无关;这只是因为List<? extends Animal>是 的超类型List<Cat>。但还有一个更简单的方法。如果您想要 a List<Animal>,并且想要使用toList(),请将流类型更改为 a Stream<Animal>

List<Animal> animals = houses.stream().map(h -> (Animal) h.cat()).toList()
Run Code Online (Sandbox Code Playgroud)

Stream::map也是通用的,所以通过让 lambda 的 RHS 为Animal, not Cat,你得到 a Stream<Animal>,然后toList()给你一个List<Animal>

如果您更喜欢的话,您也可以将其分解:

List<Animal> animals = houses.stream().map(House::cat)
                                      .map(c -> (Animal) c).toList()
Run Code Online (Sandbox Code Playgroud)

让你困惑的是,因为collect是一个泛型方法(等等Collectors::toList),所以有额外的灵活性来推断稍微不同的类型,而在更简单的流中,一切都更加明确,所以如果你想调整类型,你必须在命令式代码中执行此操作。

  • 另一个技巧是使用类型见证, `List&lt;Animal&gt; Animals = housing.stream().&lt;Animal&gt;map(House::cat).toList()` (2认同)