为什么这个 Java 8 流示例无法编译?
Why doesn't this Java 8 stream example compile?
我想弄清楚为什么这段代码不能在 JDK 1.8.0_45
:
上编译
public class Example<E extends Example<E>> {
public List<? extends Example<?>> toExamples(Collection<String> collection) {
return collection.stream()
.map(v -> lookup(v))
.collect(Collectors.toList());
}
public static <E extends Example<E>> E lookup(String value) {
return null;
}
}
添加一个看似不必要的转换修复了它:
public class Example<E extends Example<E>> {
public List<? extends Example<?>> toExamples(Collection<String> collection) {
return collection.stream()
.map(v -> (Example<?>) lookup(v))
.collect(Collectors.toList());
}
public static <E extends Example<E>> E lookup(String value) {
return null;
}
}
这是编译器的错误:
Example.java:9: error: incompatible types: inference variable R has incompatible bounds
.collect(Collectors.toList());
^
equality constraints: List<Object>
upper bounds: List<? extends Example<?>>,Object
where R,A,T are type-variables:
R extends Object declared in method <R,A>collect(Collector<? super T,A,R>)
A extends Object declared in method <R,A>collect(Collector<? super T,A,R>)
T extends Object declared in interface Stream
出于某种原因,lookup()
的 return 类型未正确推断为扩展 Example
.
的内容
我的猜测是静态方法中定义的泛型类型与class中定义的泛型类型不同。您应该能够使 lookup
方法成为非静态方法,以便它与 class 级别泛型声明中定义的相同类型相匹配:
public E lookup(String value) {
return null;
}
当你有一个 ?
时,它不等于另一个 ?
即编译器看不到
? extends Example<?>
匹配
E extends Example<E>
因为它不能假设两个 ?
是相同的。可以是
A extends Example<B>
执行转换时,您会隐藏约束以使其匹配。
由于,? extends Example<?>
与E extends Example<E>
不兼容。尽管如此,即使修复签名也无法使类型推断在这里发挥作用。
原因是类型推断的已知限制,因为它不会通过链式方法调用反向传播。换句话说,return 类型允许为 collect(…)
调用推断类型,但不能为前面的 map(…)
调用推断类型。 (另见 this answer)
但它适用于嵌套方法调用,因此可以编译以下重写的方法:
public class Example<E extends Example<E>> {
public <E extends Example<E>> List<E> toExamples(Collection<String> collection) {
return collection.stream()
.collect(Collectors.mapping(v -> lookup(v), Collectors.toList()));
}
public static <E extends Example<E>> E lookup(String value) {
return null;
}
}
不过,您必须重新考虑代码的语义。仅出现在 return 类型的方法的类型参数是不正确的,因为它暗示“无论调用者用什么替换此类型参数,该方法都会 return 正确的东西”。由于方法实现不知道调用者假设什么,所以这是不可能的。只有 returning null
或空列表才能正常工作,这没什么用。
我想弄清楚为什么这段代码不能在 JDK 1.8.0_45
:
public class Example<E extends Example<E>> {
public List<? extends Example<?>> toExamples(Collection<String> collection) {
return collection.stream()
.map(v -> lookup(v))
.collect(Collectors.toList());
}
public static <E extends Example<E>> E lookup(String value) {
return null;
}
}
添加一个看似不必要的转换修复了它:
public class Example<E extends Example<E>> {
public List<? extends Example<?>> toExamples(Collection<String> collection) {
return collection.stream()
.map(v -> (Example<?>) lookup(v))
.collect(Collectors.toList());
}
public static <E extends Example<E>> E lookup(String value) {
return null;
}
}
这是编译器的错误:
Example.java:9: error: incompatible types: inference variable R has incompatible bounds
.collect(Collectors.toList());
^
equality constraints: List<Object>
upper bounds: List<? extends Example<?>>,Object
where R,A,T are type-variables:
R extends Object declared in method <R,A>collect(Collector<? super T,A,R>)
A extends Object declared in method <R,A>collect(Collector<? super T,A,R>)
T extends Object declared in interface Stream
出于某种原因,lookup()
的 return 类型未正确推断为扩展 Example
.
我的猜测是静态方法中定义的泛型类型与class中定义的泛型类型不同。您应该能够使 lookup
方法成为非静态方法,以便它与 class 级别泛型声明中定义的相同类型相匹配:
public E lookup(String value) {
return null;
}
当你有一个 ?
时,它不等于另一个 ?
即编译器看不到
? extends Example<?>
匹配
E extends Example<E>
因为它不能假设两个 ?
是相同的。可以是
A extends Example<B>
执行转换时,您会隐藏约束以使其匹配。
由于? extends Example<?>
与E extends Example<E>
不兼容。尽管如此,即使修复签名也无法使类型推断在这里发挥作用。
原因是类型推断的已知限制,因为它不会通过链式方法调用反向传播。换句话说,return 类型允许为 collect(…)
调用推断类型,但不能为前面的 map(…)
调用推断类型。 (另见 this answer)
但它适用于嵌套方法调用,因此可以编译以下重写的方法:
public class Example<E extends Example<E>> {
public <E extends Example<E>> List<E> toExamples(Collection<String> collection) {
return collection.stream()
.collect(Collectors.mapping(v -> lookup(v), Collectors.toList()));
}
public static <E extends Example<E>> E lookup(String value) {
return null;
}
}
不过,您必须重新考虑代码的语义。仅出现在 return 类型的方法的类型参数是不正确的,因为它暗示“无论调用者用什么替换此类型参数,该方法都会 return 正确的东西”。由于方法实现不知道调用者假设什么,所以这是不可能的。只有 returning null
或空列表才能正常工作,这没什么用。