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
的实例,将返回<