为什么这个 Java 8 流示例不能编译?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/30599566/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why doesn't this Java 8 stream example compile?
提问by Stijn Van Bael
I'm trying to figure out why this code does not compile on JDK 1.8.0_45
:
我想弄清楚为什么这段代码不能在 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;
}
}
Adding a seemingly unnecessary cast fixes it:
添加一个看似不必要的演员表修复它:
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;
}
}
Here's the error from the compiler:
这是编译器的错误:
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
For some reason, the return type of lookup()
isn't correctly inferred to something extending Example
.
出于某种原因, 的返回类型lookup()
没有正确推断为扩展Example
.
回答by Holger
As Peter Lawrey pointed out, ? extends Example<?>
is not compatible with E extends Example<E>
. Still, even fixing the signature doesn't make type inference work here.
正如Peter Lawrey 所指出的,? extends Example<?>
与E extends Example<E>
. 尽管如此,即使修复签名也不会使类型推断在这里起作用。
The reason is a known limitation of the type inference as it does not back-propagate through chained method invocations. In other words, the return type allows to infer the types for the collect(…)
invocation but not for the preceding map(…)
invocation. (see also this answer)
原因是类型推断的已知限制,因为它不会通过链式方法调用进行反向传播。换句话说,返回类型允许推断collect(…)
调用的类型,但不能推断先前map(…)
调用的类型。(另见这个答案)
But it works for nestedmethod invocations, so the following rewritten method can be compiled:
但它适用于嵌套方法调用,因此可以编译以下重写的方法:
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;
}
}
Still, you have to rethink the semantics of your code. A method's type parameter which appears only at the return type can't be correct as it implies that “whatever the caller substitutes for this type parameter, the method will return the right thing”. Since the method implementation doesn't know what the caller assumes, this is impossible. Only returning null
or an empty list will work correctly, which is of little use.
不过,您必须重新考虑代码的语义。只出现在返回类型的方法的类型参数不可能是正确的,因为它暗示“无论调用者替换这个类型参数,方法都会返回正确的东西”。由于方法实现不知道调用者假设什么,这是不可能的。只有返回null
或空列表才能正常工作,这没什么用。
回答by Peter Lawrey
When you have a ?
it doesn't equal another ?
i.e. the compiler doesn't see
当你有一个?
它不等于另一个,?
即编译器看不到
? extends Example<?>
as a match for
作为匹配
E extends Example<E>
as it cannot assume the two ?
are the same. It could be
因为它不能假设两者?
是相同的。它可能是
A extends Example<B>
When you perform the cast, you obscure the constraint so it can match.
当您执行强制转换时,您会模糊约束以使其匹配。
回答by John Vint
My guess is that the generic type defined in the static method is not the same as the generic type defined in the class. You should be able to make the lookup
method non-static so it matches the same type defined in the class level generic declaration:
我的猜测是静态方法中定义的泛型类型与类中定义的泛型类型不一样。您应该能够使该lookup
方法成为非静态方法,以便它与类级别泛型声明中定义的相同类型相匹配:
public E lookup(String value) {
return null;
}