为什么Streams API在这种情况下需要提示泛型类型? [英] Why does the Streams API need a hint for generic type in this case?

查看:177
本文介绍了为什么Streams API在这种情况下需要提示泛型类型?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述



  @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编译时,错误是:

 错误:(809,94)java:不兼容的类型:
java.lang.Object不能转换为
@ org.jetbrains.annotations.NotNull java.lang.String

但下面的代码很好:

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

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



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

解决方案

这个答案的这个顶部部分基本上是 Radiodef 所说的上面的评论。我不想窃取这些单词,但在 --- 下面的答案在没有事先说明的情况下并不真正起作用。



正如Radiodef指出的那样,这在第一种情况下不起作用的原因是因为它使用原始类型 Collection 。相反,使用集合<> ,它就可以工作:

 返回((集合<>)值).stream()
.map(MyClass :: defaultFormatter)
.collect(Collectors.joining(eol));

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

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

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


从原始类或接口类型(§4.8) G 到任何参数化类型 G < ; T1,...,Tn>







没有变量的情况下你不能这样做的原因有点微妙。 此答案更直接地解决了这个问题:当您使用原始类型时,所有泛型都将被删除从类型来看,不仅仅是与省略类型直接相关的类型。

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


  • Stream.collect(Collector< ;? super T,A,R> collector)返回一个 R ;

  • 删除 R Object

  • ul>

    ,因此 collect 调用的返回类型为 Object ,你在这里观察由于它不是 List

    ,所以不能通过未经检查的转换自动强制为 List >

    The following fails to compile:

        @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);
        }
    

    In particular, when compiled with javac, the error would be:

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

    But the following compiles just fine:

        @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);
        }
    

    The only difference would be that I introduced an extra variable. Note that I didn't cast, so no semantic change.

    Can anybody explain why is this needed?

    解决方案

    This top part of this answer is basically what Radiodef said in comments above. I'm not wanting to steal those words, but the answer below the --- doesn't really work without the prior explanation.

    As pointed out by Radiodef, the reason why this doesn't work in the first case is because it's using a raw type, Collection. Instead, use Collection<?>, and it will work:

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

    The reason why it works with the explicit variable is because of unchecked conversion. Note that the following produces an unchecked conversion warning:

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

    The actual type of the expression on the RHS is Stream; you're allowed to coerce that to a Stream<String>, as described in JLS Sec 5.1.9:

    There is an unchecked conversion from the raw class or interface type (§4.8) G to any parameterized type of the form G<T1,...,Tn>.


    The reason why you can't do the same without the variable is a bit more subtle. This answer addresses the issue more directly: when you use a raw type, all generics are erased from the type, not just ones directly related to the omitted type.

    So, the type of Stream.collect when the Stream is raw is the erasure of the type when it is generic:

    • Stream.collect(Collector<? super T,A,R> collector) returns an R;
    • The erasure of R is Object

    so the return type of the collect call is Object, as you observe here. This can't be automatically coerced to a List<String> via unchecked conversion because it's not List.

    这篇关于为什么Streams API在这种情况下需要提示泛型类型?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆