Java自身类型递归类型参数和javac中的继承错误

Jes*_*sse 8 java generics javac java-6 java-7

为什么这段代码不能编译?

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
    {
    }
}
Run Code Online (Sandbox Code Playgroud)

Java 6在handle(new ModifiedThingA());以下版本中给出了此错误:

x.java:6: <T,X>handle(X) in x cannot be applied to (x.ModifiedThingA)
            handle(new ModifiedThingA());
            ^
Run Code Online (Sandbox Code Playgroud)

Java 7甚至不喜欢handle(new ThingA());,这是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
Run Code Online (Sandbox Code Playgroud)

在我看来,那javac是弄错ModifiedThingABaseThing<ModifiedThingA>,当它实际上是一个BaseThing<ThingA>.这是我的虫子还是javac

Ben*_*ulz 1

javac的行为看来是正确的。理论上,一个类型变量T就足够了。但是,您引入了第二个类型变量X来帮助进行类型推断。首先推断for 的参数X,然后根据调用上下文T推断 for 的参数:

List<ThingA> a = handle(new ThingA());
List<ThingA> b = handle(new ModifiedThingA());
Run Code Online (Sandbox Code Playgroud)

但是,您的调用上下文不会对返回类型设置任何限制。因此,编译器被迫引入一个CAP#1以 null 类型作为下限的类型变量 ( )。T的论证将被推断为glb(BaseThing<CAP#1>) = BaseThing<CAP#1>。鉴于其下限,X不能证明 是 的子类型T

有两三种方法可以解决这个问题。

  1. 手动推断(如果很少需要的话也可以)
  2. 提供返回类型为 void 的“重载”(需要另一个名称,呃)
  3. 如果返回的列表是不可变的或防御性副本,您可以断开类型参数T与其边界参数的连接

我更喜欢选项 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());
}
Run Code Online (Sandbox Code Playgroud)

快乐的仿制药。