在这种情况下,为什么 Streams API 需要泛型类型的提示?

ddi*_*rov 5 java generics inferred-type java-stream

以下情况无法编译:

    @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);
    }
Run Code Online (Sandbox Code Playgroud)

特别是,当使用 javac 编译时,错误将是:

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

但以下编译得很好:

    @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);
    }
Run Code Online (Sandbox Code Playgroud)

唯一的区别是我引入了一个额外的变量。请注意,我没有进行强制转换,因此没有语义更改。

有人能解释为什么需要这个吗?

And*_*ner 4

这个答案的上面部分基本上就是Radiodef在上面的评论中所说的。我不想窃取这些话,但如果---没有事先解释,下面的答案就不会真正起作用。

\n\n

正如 Radiodef 所指出的,这在第一种情况下不起作用的原因是因为它使用原始类型Collection. 相反,使用Collection<?>,它将起作用:

\n\n
        return ((Collection<?>) value).stream()\n                    .map(MyClass::defaultFormatter)\n                    .collect(Collectors.joining(eol));\n
Run Code Online (Sandbox Code Playgroud)\n\n

它与显式变量一起使用的原因是由于未经检查的转换。请注意,以下内容会产生未经检查的转换警告:

\n\n
        Stream<String> stream = ((Collection) value).stream()\n                     .map(MyClass::defaultFormatter);\n
Run Code Online (Sandbox Code Playgroud)\n\n

RHS 上表达式的实际类型是Stream;您可以将其强制转换为 a Stream<String>,如JLS Sec 5.1.9中所述:

\n\n
\n

存在从原始类或接口类型 (\xc2\xa74.8)G到任何形式的参数化类型的未经检查的转换G<T1,...,Tn>

\n
\n\n
\n\n

没有变量就不能做同样的事情的原因有点微妙。这个答案更直接地解决了这个问题:当您使用原始类型时,所有泛型都会从类型中删除,而不仅仅是与省略类型直接相关的泛型。

\n\n

Stream.collect因此,原始类型时的类型Stream是通用类型时的擦除:

\n\n
    \n
  • Stream.collect(Collector<? super T,A,R> collector)返回一个R;
  • \n
  • 的擦除RObject
  • \n
\n\n

所以调用的返回类型collectObject,正如您在此处观察到的那样。这不能自动强制为List<String>通过未经检查的转换,因为它不是List

\n