繁体   English   中英

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

[英]Java self type recursive type parameters and inheritance error in javac

为什么此代码无法编译?

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在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甚至不喜欢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

这在我看来, javac被误认为ModifiedThingABaseThing<ModifiedThingA>时,它实际上是一个BaseThing<ThingA> 这是我的bug还是javac

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

List<ThingA> a = handle(new ThingA());
List<ThingA> b = handle(new ModifiedThingA());

但是,您的调用上下文不会对返回类型施加任何限制。 因此,编译器被迫引入以空类型为下限的类型变量( CAP#1 )。 T的参数将推断为glb(BaseThing<CAP#1>) = BaseThing<CAP#1> 给定其下限, X不能证明是T的子类型。

有两种或三种方法可以解决此问题。

  1. 手动推断(如果很少需要,可以)
  2. 提供返回类型为void的“过载”(需要另一个名称,urgh)
  3. 如果返回的列表是不可变的或防御性的副本,则可以将类型参数T与它的bounds参数断开连接

我更喜欢选项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());
}

快乐的仿制药。

您的代码可以在javac 1.8.0_45和Eclipse 4.1.1中很好地编译。

为了更好地编译lambda表达式,对Java 8中的类型推断算法所做的更改可能也解决了您的问题。

javac 1.7.0_09和1.7.0_11中似乎存在一个错误,这会导致此问题。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM