【问题标题】:Why does the Streams API need a hint for generic type in this case?在这种情况下,为什么 Streams API 需要泛型类型的提示?
【发布时间】:2017-11-25 19:30:02
【问题描述】:

以下编译失败:

    @NotNull String defaultFormatter(@Nullable Object value) {
        if (value instanceof Collection) {
            return ((Collection) value).stream()
                        .map(MyClass::defaultFormatter)
                        .collect(Collectors.joining(eol));
        }
        return String.valueOf(value);
    }

尤其是使用 javac 编译时,错误会是:

Error:(809, 94) java: incompatible types: 
      java.lang.Object cannot be converted to 
      @org.jetbrains.annotations.NotNull java.lang.String

但是下面的编译就好了:

    @NotNull String defaultFormatter(@Nullable Object value) {
        if (value instanceof Collection) {
            Stream<String> stream = ((Collection) value).stream()
                         .map(MyClass::defaultFormatter);
            return stream.collect(Collectors.joining(eol));
        }
        return String.valueOf(value);
    }

唯一的区别是我引入了一个额外的变量。请注意,我没有强制转换,所以没有语义变化。

谁能解释为什么需要这样做?

【问题讨论】:

  • 您使用的是javac 还是IDE 的编译器?
  • IntelliJ IDEA 的语法高亮,并通过 javac 编译确认。
  • stackoverflow.com/questions/2770321/… 的可能重复项。请参阅“原始类型是该类型的擦除”下的部分。您需要使用Collection&lt;?&gt; 而不是Collection
  • 由于unchecked conversion,该作业有效。基本上任何AnObject&lt;T&gt; = AnObjectAnObject = AnObject&lt;T&gt; 形式的赋值都是有效的,原因是向后兼容旧库。
  • @ddimitrov 鼓励 Radiodef 将其写成答案。并在一些脾气暴躁的灵魂带着欺骗锤出现并将其标记为“什么是原始类型”的欺骗之前做到这一点。

标签: java generics java-stream inferred-type


【解决方案1】:

这个答案的上面部分基本上是Radiodef 在上面的 cmets 中所说的。我不想盗用这些话,但如果没有事先的解释,--- 下面的答案实际上是行不通的。

正如 Radiodef 所指出的,这在第一种情况下不起作用的原因是因为它使用的是原始类型,Collection。相反,使用Collection&lt;?&gt;,它会起作用:

        return ((Collection<?>) value).stream()
                    .map(MyClass::defaultFormatter)
                    .collect(Collectors.joining(eol));

它与显式变量一起工作的原因是未经检查的转换。请注意,以下会产生未经检查的转换警告:

        Stream<String> stream = ((Collection) value).stream()
                     .map(MyClass::defaultFormatter);

RHS 上表达式的实际类型是Stream;如JLS Sec 5.1.9 中所述,您可以将其强制为Stream&lt;String&gt;

存在从原始类或接口类型(第 4.8 节)GG&lt;T1,...,Tn&gt; 形式的任何参数化类型的未经检查的转换。


没有变量你不能做同样的事情的原因有点微妙。 This answer addresses the issue more directly:当你使用原始类型时,所有泛型都会从类型中删除,而不仅仅是与省略的类型直接相关的泛型。

因此,Stream 为 raw 时的 Stream.collect 类型是泛型时类型的擦除:

  • Stream.collect(Collector&lt;? super T,A,R&gt; collector) 返回 R
  • R的擦除是Object

所以collect 调用的返回类型是Object,正如您在此处观察到的。这不能通过未经检查的转换自动强制转换为List&lt;String&gt;,因为它不是List

【讨论】:

    猜你喜欢
    • 2022-11-27
    • 2013-07-22
    • 1970-01-01
    • 2020-12-02
    • 1970-01-01
    • 2016-08-08
    • 1970-01-01
    • 2011-04-27
    • 2016-07-17
    相关资源
    最近更新 更多