[英]Java self type recursive type parameters and inheritance error in javac
Why does this code not compile? 为什么此代码无法编译?
public class x
{
private void test()
{
handle(new ThingA());
handle(new ModifiedThingA());
}
private <T extends BaseThing<T>, X extends T> java.util.List<T> handle(X object)
{
return object.getList();
}
private static class BaseThing<T extends BaseThing<T>>
{
public java.util.List<T> getList()
{
return null;
}
}
private static class ThingA
extends BaseThing<ThingA>
{
}
private static class ModifiedThingA
extends ThingA
{
}
}
Java 6 gives this this error in handle(new ModifiedThingA());
Java 6在
handle(new ModifiedThingA());
给出了此错误handle(new ModifiedThingA());
: :
x.java:6: <T,X>handle(X) in x cannot be applied to (x.ModifiedThingA)
handle(new ModifiedThingA());
^
Java 7 does not even like handle(new ThingA());
Java 7甚至不喜欢
handle(new ThingA());
, this is the Java 7 output: ,这是Java 7的输出:
x.java:5: error: invalid inferred types for T; inferred type does not conform to declared bound(s)
handle(new ThingA());
^
inferred: ThingA
bound(s): CAP#1
where T,X are type-variables:
T extends BaseThing<T> declared in method <T,X>handle(X)
X extends T declared in method <T,X>handle(X)
where CAP#1 is a fresh type-variable:
CAP#1 extends BaseThing<CAP#1> from capture of ?
x.java:6: error: invalid inferred types for T; inferred type does not conform to declared bound(s)
handle(new ModifiedThingA());
^
inferred: ModifiedThingA
bound(s): CAP#1
where T,X are type-variables:
T extends BaseThing<T> declared in method <T,X>handle(X)
X extends T declared in method <T,X>handle(X)
where CAP#1 is a fresh type-variable:
CAP#1 extends BaseThing<CAP#1> from capture of ?
2 errors
It seems to me that javac
is mistaking ModifiedThingA
for a BaseThing<ModifiedThingA>
when it is in fact a BaseThing<ThingA>
. 这在我看来,
javac
被误认为ModifiedThingA
的BaseThing<ModifiedThingA>
时,它实际上是一个BaseThing<ThingA>
Is this my bug or javac
's? 这是我的bug还是
javac
?
javac
's behavior appears to be correct. javac
的行为似乎是正确的。 Theoretically one type variable, T
, would be sufficient. 从理论上讲,一个类型变量
T
就足够了。 However, you introduced a second type variable, X
, to help type inference along. 但是,您引入了第二个类型变量
X
,以帮助进行类型推断。 The argument for X
gets inferred first and then, based on the invocation context, the argument for T
is inferred: 首先推断
X
的参数,然后根据调用上下文推断T
的参数:
List<ThingA> a = handle(new ThingA());
List<ThingA> b = handle(new ModifiedThingA());
However, your invocation context does not put any bounds on the return type. 但是,您的调用上下文不会对返回类型施加任何限制。 Thus the compiler is forced to introduce a type variable (
CAP#1
) with the null type as lower bound. 因此,编译器被迫引入以空类型为下限的类型变量(
CAP#1
)。 T
's argument will be inferred as glb(BaseThing<CAP#1>) = BaseThing<CAP#1>
. T
的参数将推断为glb(BaseThing<CAP#1>) = BaseThing<CAP#1>
。 Given its lower bound, X
is not provably a subtype of T
. 给定其下限,
X
不能证明是T
的子类型。
There are two or three ways out of this. 有两种或三种方法可以解决此问题。
T
from its bounds argument T
与它的bounds参数断开连接 I prefer option 3: 我更喜欢选项3:
private <T extends BaseThing<T>> List<T> handle(BaseThing<? extends T> object) {
return new ArrayList<T>(object.getList());
// or (using guava's ImmutableList)
return ImmutableList.copyOf(object.getList());
}
Happy generics. 快乐的仿制药。
Your code compiles fine in javac 1.8.0_45 and Eclipse 4.1.1. 您的代码可以在javac 1.8.0_45和Eclipse 4.1.1中很好地编译。
Probably the changes that where made to the type inference algorithm in Java 8 in order to compile lambda expressions in a nice way also solved your problem. 为了更好地编译lambda表达式,对Java 8中的类型推断算法所做的更改可能也解决了您的问题。
javac 1.7.0_09和1.7.0_11中似乎存在一个错误,这会导致此问题。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.