为什么在这种情况下 Streams API 需要泛型类型的提示?
Why does the Streams API need a hint for generic type in this case?
以下编译失败:
@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);
}
唯一的区别是我引入了一个额外的变量。请注意,我没有投射,所以没有语义变化。
谁能解释为什么需要这个?
这个答案的顶部基本上是 Radiodef 在上面的评论中所说的。我不想窃取这些话,但如果没有事先解释,---
下面的答案实际上不起作用。
正如 Radiodef 所指出的,第一种情况下这不起作用的原因是因为它使用的是原始类型,Collection
。相反,使用 Collection<?>
,它将起作用:
return ((Collection<?>) value).stream()
.map(MyClass::defaultFormatter)
.collect(Collectors.joining(eol));
之所以使用显式变量是因为未经检查的转换。请注意,以下会产生未经检查的转换警告:
Stream<String> stream = ((Collection) value).stream()
.map(MyClass::defaultFormatter);
RHS 上表达式的实际类型是Stream
;您可以将其强制转换为 Stream<String>
,如 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>
.
没有变量你不能做同样的事情的原因有点微妙。 This answer addresses the issue more directly:当您使用原始类型时,所有 泛型都会从类型中删除,而不仅仅是与省略的类型直接相关的泛型。
所以,当 Stream
是原始类型时,Stream.collect
的类型是泛型时类型的擦除:
Stream.collect(Collector<? super T,A,R> collector)
return 一个 R
;
R
的擦除是Object
所以 collect
调用的 return 类型是 Object
,正如您在此处观察到的那样。这不能通过未经检查的转换自动强制转换为 List<String>
,因为它不是 List
.
以下编译失败:
@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);
}
唯一的区别是我引入了一个额外的变量。请注意,我没有投射,所以没有语义变化。
谁能解释为什么需要这个?
这个答案的顶部基本上是 Radiodef 在上面的评论中所说的。我不想窃取这些话,但如果没有事先解释,---
下面的答案实际上不起作用。
正如 Radiodef 所指出的,第一种情况下这不起作用的原因是因为它使用的是原始类型,Collection
。相反,使用 Collection<?>
,它将起作用:
return ((Collection<?>) value).stream()
.map(MyClass::defaultFormatter)
.collect(Collectors.joining(eol));
之所以使用显式变量是因为未经检查的转换。请注意,以下会产生未经检查的转换警告:
Stream<String> stream = ((Collection) value).stream()
.map(MyClass::defaultFormatter);
RHS 上表达式的实际类型是Stream
;您可以将其强制转换为 Stream<String>
,如 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 formG<T1,...,Tn>
.
没有变量你不能做同样的事情的原因有点微妙。 This answer addresses the issue more directly:当您使用原始类型时,所有 泛型都会从类型中删除,而不仅仅是与省略的类型直接相关的泛型。
所以,当 Stream
是原始类型时,Stream.collect
的类型是泛型时类型的擦除:
Stream.collect(Collector<? super T,A,R> collector)
return 一个R
;R
的擦除是Object
所以 collect
调用的 return 类型是 Object
,正如您在此处观察到的那样。这不能通过未经检查的转换自动强制转换为 List<String>
,因为它不是 List
.