简体   繁体   English

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

[英]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被误认为ModifiedThingABaseThing<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. 有两种或三种方法可以解决此问题。

  1. manually infer (okay if rarely necessary) 手动推断(如果很少需要,可以)
  2. provide an "overload" with return type void (needs another name, urgh) 提供返回类型为void的“过载”(需要另一个名称,urgh)
  3. if the returned list is immutable or a defensive copy, you can disconnect the type argument 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.

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