为什么超类字段的泛型类型不会被擦除到子类型的具体绑定中?

时间:2018-07-08 22:07:40

标签: java generics reflection type-erasure

package ir.openuniverse;

public class Main {
    public static void main(String[] args) throws NoSuchFieldException {
        System.out.println(A.class.getField("t").getType().getName());
    }
}

class A extends B<D> {}
class B<T extends C> {
    public T t;
}

class C {}
class D extends C {}

输出为ir.openuniverse.C为什么?我希望有D


编辑:

此问题与解决方法或替代方法无关。因此,答案与解决方法无关。有关其他方法,请参见下面的myself answer

3 个答案:

答案 0 :(得分:5)

发生这种情况是由于type erasure

Java将您的泛型类B<T>编译为字节代码,适合与所有可以引用它的类一起使用,包括可能扩展了B<T>的任何类。

由于T仅限于扩展C的类,因此Java知道您可以分配B.t的任何值都将扩展C,因此它将类编译成等效的类

class B {
    C t;
}

此时,t的任何分配都可以使用;不过,从t读取将产生C,因此编译器必须做一些“魔术”来解决此问题。具体地说,编译器在已知子类型的地方插入类型强制转换。如有必要,它可能还会生成 bridge方法。有关详细信息,请参见答案顶部的链接。

答案 1 :(得分:1)

在编译过程中,Java的类型擦除更改

contentView

至:

contentView.addSubview(rankingLabel)
contentView.addConstraints(cellConstraints)

由于class B<T extends C> { public T t; } 标识了字段的声明类型,所以输出为class B<C> { public C t; }

答案 2 :(得分:0)

感谢大家的帮助。最后,我不得不将A的定义更改为:

class A extends B<D> {
    public D t;
}

满足我的目的就足够了(尽管我一点都不喜欢它!)。


编辑:

上述方法不是基本方法。请参阅下面的 @DanielPryden 的前两个注释。

替代解决方法:

class A extends B<D> {
    @Override public D getT() { return super.getT(); }
}

class B<T extends C> {
    private T t;
    public T getT() { return t; }
}

main()中:

System.out.println(A.class.getMethod("getT").getReturnType().getName());

输出:ir.openuniverse.D


另一种方式

  

注意:这不是解决主要问题的方法(请参见下面的 @DanielPryden 的第四条评论)。但是也许可以帮助您(像我一样)。

当您至少有一个A实例时,可以使用此解决方法:

public class Main {
    public static void main(String[] args) {
        A a = new A();
        System.out.println(a.t.getClass().getName());
        // Or via reflection:
        // System.out.println(a.getClass().getField("t").get(a).getClass().getName());
    }
}

class A extends B<D> {
    { t = new D(); }
}

class B<T extends C> {
    public T t;
}

输出:ir.openuniverse.D