【问题标题】:Template parameter difference between Collectors.toList and Stream.toListCollectors.toList 和 Stream.toList 之间的模板参数区别
【发布时间】:2021-09-11 23:29:25
【问题描述】:

发现 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
    }
}

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

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

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

【问题讨论】:

  • 您似乎知道为什么toList 无法编译,因为它的签名具有通用性。我不确定你在问什么。
  • 帖子编辑为问题。

标签: java java-16


【解决方案1】:

这里有一个简单的答案。

开始

var stream = houses.stream().map(House::cat)

这里,stream 的类型为 Stream&lt;Cat&gt;Stream::toList 方法给你一个流元素类型的列表,这里是Cat。所以stream.toList() 的类型是List&lt;Cat&gt;。这里别无选择。

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

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

List<? extends Animal> animals2 = houses.stream().map(House::cat).toList()

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

List<Animal> animals = houses.stream().map(h -> (Animal) h.cat()).toList()

Stream::map 也是通用的,因此通过将 lambda 的 RHS 设置为 Animal,而不是 Cat,您会得到一个 Stream&lt;Animal&gt;,然后 toList() 会给您一个 List&lt;Animal&gt;

如果你更喜欢的话,你也可以把它拆开:

List<Animal> animals = houses.stream().map(House::cat)
                                      .map(c -> (Animal) c).toList()

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

【讨论】:

  • 另一个技巧是使用类型见证,List&lt;Animal&gt; animals = houses.stream().&lt;Animal&gt;map(House::cat).toList()
【解决方案2】:

这是不可能实现的。

Collectors::toList 有一个? extends T 的声明。另一方面,Stream::toList 返回一个List&lt;T&gt;,你就卡住了。

您可以通过以下方式(部分)解决该问题:

List<? extends Animal> animals2 = houses.stream().map(House::cat).toList();

您的想法是U super T,但不幸的是,这不受支持。人们偶尔会发现它的良好实际用例 - 但没有支持。

【讨论】:

    猜你喜欢
    • 2021-05-04
    • 1970-01-01
    • 2021-04-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-04
    • 1970-01-01
    相关资源
    最近更新 更多