Java 为什么超类字段的泛型类型不会被擦除到子类型中的具体绑定? package ir.openuniverse; 公共班机{ 公共静态void main(字符串[]args)引发NoSuchFieldException{ System.out.println(A.class.getField(“t”).getType().getName()); } } 类A扩展了B{} B类{ 公共交通; } 类C{} 类D扩展了C{}

Java 为什么超类字段的泛型类型不会被擦除到子类型中的具体绑定? package ir.openuniverse; 公共班机{ 公共静态void main(字符串[]args)引发NoSuchFieldException{ System.out.println(A.class.getField(“t”).getType().getName()); } } 类A扩展了B{} B类{ 公共交通; } 类C{} 类D扩展了C{},java,generics,reflection,type-erasure,Java,Generics,Reflection,Type Erasure,输出为ir.openuniverse.C为什么?我期望D 编辑: 这个问题不是关于变通办法或替代方法。所以答案不是关于变通办法。有关替代方法,请参见下文。这是因为 Java将泛型类B编译成字节码,适合与所有可能引用它的类一起使用,包括任何可能扩展B的类 由于T仅限于扩展C的类,因此Java知道您可以分配B.T的任何值都将扩展C,因此它将您的类编译成等价的类 package ir.openuniverse; public class Main { public static void

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


编辑:

这个问题不是关于变通办法或替代方法。所以答案不是关于变通办法。有关替代方法,请参见下文。

这是因为

Java将泛型类
B
编译成字节码,适合与所有可能引用它的类一起使用,包括任何可能扩展
B
的类

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

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 {}
此时,
t
的任何赋值都将起作用;但是,从
t
读取将产生
C
,因此编译器必须执行一些“魔法”来修复此问题。具体来说,编译器在子类型已知的位置插入类型强制转换。如有必要,它还可以生成桥接方法。有关详细信息,请参阅答案顶部的链接。

发生这种情况的原因是

Java将泛型类
B
编译成字节码,适合与所有可能引用它的类一起使用,包括任何可能扩展
B
的类

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

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 {}

此时,
t
的任何赋值都将起作用;但是,从
t
读取将产生
C
,因此编译器必须执行一些“魔法”来修复此问题。具体来说,编译器在子类型已知的位置插入类型强制转换。如有必要,它还可以生成桥接方法。有关详细信息,请参见答案顶部的链接。

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

class B {
    C t;
}
B类{
公共交通;
}
致:

B类{
公共CT;
}

由于
getType()
标识字段的声明类型,因此在编译期间,输出是
ir.openuniverse.C

,Java的类型擦除会发生变化

class B {
    C t;
}
B类{
公共交通;
}
致:

B类{
公共CT;
}

由于
getType()
标识字段的声明类型,因此输出为
ir.openuniverse.C

感谢所有人的帮助。最后,我被迫将我的
A
的定义更改为:

class B<C> {
public C t;
}
输出:
ir.openuniverse.D


另一种方式:

注:这并不是主要问题的解决方案(请参见下文@DanielPryden的第四条评论)。但也许能帮助你(像我一样)

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

System.out.println(A.class.getMethod("getT").getReturnType().getName());
公共类主{
公共静态void main(字符串[]args){
A=新的A();
System.out.println(a.t.getClass().getName());
//或通过反射:
//System.out.println(a.getClass().getField(“t”).get(a.getClass().getName());
}
}
A类延伸至B类{
{t=newd();}
}
B类{
公共交通;
}

输出:
ir.openuniverse.D

感谢所有人的帮助。最后,我被迫将我的
A
的定义更改为:

class B<C> {
public C t;
}
输出:
ir.openuniverse.D


另一种方式:

注:这并不是主要问题的解决方案(请参见下文@DanielPryden的第四条评论)。但也许能帮助你(像我一样)

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

System.out.println(A.class.getMethod("getT").getReturnType().getName());
公共类主{
公共静态void main(字符串[]args){
A=新的A();
System.out.println(a.t.getClass().getName());
//或通过反射:
//System.out.println(a.getClass().getField(“t”).get(a.getClass().getName());
}
}
A类延伸至B类{
{t=newd();}
}
B类{
公共交通;
}

输出:
ir.openuniverse.D

请编辑您的标题来描述您的实际问题。它比这更微妙:在
B
的擦除中
T
的类型是独立于
A
的上下文确定的,其中
T
的范围较窄。如果你想一想,一定是,因为编译器编译
B
时不知道
A
。因为编译时它已经被删除到了自己的下限。请编辑标题来描述您的实际问题。它比这更微妙:在
B
的删除中
T
的类型是独立于
A
,其中
T
的界限较窄。如果你想一想,它必须是这样的,因为编译器编译
B
,而不知道
A
。因为它在编译时已经被删除到它自己的下限。那么
类A
呢?
扩展了B
类A扩展了B{}
中没有类型擦除,因为它不使用泛型类型参数
类A
呢?
扩展了B
类A扩展了B{}
中没有类型擦除,因为它不使用泛型类型参数,这可能不会像您认为的那样,因为这意味着
A.t
B.t
将是两个不同的字段,您需要做的可能是将
B.t
设置为私有字段,但添加一个公共访问器方法,例如
public final t getT(){return t;}
。对于
A
的实例,将返回<