在Java中,在运行时使用泛型-(T)对象进行强制转换时会发生什么情况?

在Java中,在运行时使用泛型-(T)对象进行强制转换时会发生什么情况?,java,class,generics,compiler-construction,casting,Java,Class,Generics,Compiler Construction,Casting,据我所知,Java泛型会删除泛型方法(或泛型类)中与参数类型T有关的所有信息。这就是为什么我们不能使用 新表达式,例如 new T() if(obj instanceof T) 表达式的实例,例如 new T() if(obj instanceof T) 在泛型方法中 我的问题是,当涉及到强制转换时,参数类型T在泛型方法中是如何工作的。例如,我这里有3个简单的类: public class People { String name; public String get

据我所知,Java泛型会删除泛型方法(或泛型类)中与参数类型T有关的所有信息。这就是为什么我们不能使用

新表达式,例如

new T()
if(obj instanceof T)
表达式的实例,例如

new T()
if(obj instanceof T)
在泛型方法中

我的问题是,当涉及到强制转换时,参数类型T在泛型方法中是如何工作的。例如,我这里有3个简单的类:

public class People {

    String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public People(String name) {
        super();
        this.name = name;
    }

    @Override
    public String toString() {
        return "People [name=" + name + "]";
    }

}

public class GenericClass<T> {

    public T getCast(People p) {
        return (T)p;
    }
}

public class App 
{
    public static void main( String[] args )
    {
        People p = new People("Kewei");

        GenericClass<Integer> genericClass = new GenericClass<Integer>();
        Object p_object = genericClass.getCast(p);

        System.out.println(p_object);
    }
}
公共阶层人士{
字符串名;
公共字符串getName(){
返回名称;
}
公共void集合名(字符串名){
this.name=名称;
}
公众人物(字符串名称){
超级();
this.name=名称;
}
@凌驾
公共字符串toString(){
返回“People[name=“+name+”]”;
}
}
公共类泛型类{
公共T getCast(人物p){
返回(T)p;
}
}
公共类应用程序
{
公共静态void main(字符串[]args)
{
人员p=新人员(“科维”);
GenericClass GenericClass=新的GenericClass();
Object p_Object=genericClass.getCast(p);
System.out.println(p_对象);
}
}
当它在通用方法
getCast(People p)
中执行
(T)p
时。它只是把类型T作为对象吗?或者它只是在编译时删除整个
(T)
强制转换?我读过Bruce Eckel的《Java思维》,从中我大致理解为:

Java编译器在编译时在方法的入口/出口点检查 确保泛型方法和插入的内部一致性 转换代码(在编译时)

这是正确的理解吗


谢谢。

Java从不抛出任何东西;在运行时,它被简单地视为“一个对象”。 JRE尝试调用该对象上的方法,并且由于在编译时已证明存在这些方法,因此它可以工作


没有对运行时T类的引用。因此,不需要进行转换。

你可以想象编译器将带有泛型的代码“重新写入”到没有泛型的代码中

在泛型类中,T会被擦除到其上界的擦除,在本例中,上界是Object,但也可能是其他对象。对T的强制转换成为对T的上界的强制转换(在本例中为Object,因此它是多余的,编译器可能会跳过它)。该方法同样返回T的上界(即Object)。因此,没有运行时检查方法内部的强制转换是否有效

public class GenericClass {

    public Object getCast(People p) {
        // In this case the cast to T is removed because T's upper bound is Object.
        // But if T's upper bound was, say, Number, then there would still be a cast
        // to the upper bound. e.g. "(Number)p", and the return type would also be Number
        return p;
    }
}
在调用返回T的方法的地方,编译器将向实际类型的T插入一个强制转换。在这种情况下,在main方法中,您有一个
GenericClass
,因此当
getCast
返回T时,它被强制转换为整数。因此,在本例中,运行时检查现在被“移动”到调用方法,而不是带有强制转换的方法

// somewhere else
GenericClass genericClass = new GenericClass();
p_object = (Integer)genericClass.getCast(p);

调用方还可能保留一个
GenericClass
引用(对于其作用域中的某些类型参数S),以便将结果强制转换为S,而不是特定类型。然后,我们将再次重复整个重写过程(即,对S的强制转换将被重新写入到其上限的强制转换,并且调用该方法的位置将结果强制转换为其实际类型参数)。这意味着运行时检查可能会推迟到另一个地方,以此类推。

不正确。对普通类的强制转换也会在运行时进行检查。不过,我们仍然需要检查强制转换和赋值。i、 e.
列表;字符串s=list.get(0)
将动态检查对象是否为字符串。这是一个可预测的
ld/cmp/br
序列(即,如果我们不丢失内存,则为一个周期)。是的,我的意思是泛型类本身没有对t的引用,也没有强制转换。我的回复写得非常糟糕。+1这就是为什么堆污染导致的
ClassCastException
s更难调试的原因-因为它们的stacktrace并不指向实际的bug,而是编译器插入强制转换的地方。我想你指的是上界,而不是下界。另外,它是上界的擦除,也就是上界最左边的元素的擦除。非常感谢@newacct,谢谢你的解释。正如您所说,在调用返回T的方法的地方,编译器将向实际类型的T插入一个强制转换。在本例中,在main方法中,您有一个GenericClass,因此当getCast返回T时,它被强制转换为Integer。但是无论如何,对于我的方法
T getCast(People p)
People
的一个实例不能被转换成
Integer
,我不明白我的代码是如何“神奇地”工作的。为什么它不抛出一个
ClassCastException
?似乎
p
People
)的实例从未被转换成
Integer
。为什么?@keweishang:我不确定,但我猜这可能与它被分配给
Object
类型的变量有关。如果你把它改成
整数p_object
会有区别吗?@keweishang:1。它返回T被擦除的内容,在本例中是Object。2.如果返回的类型(擦除)不可分配给您正在分配或传递该值的对象的类型,则需要强制转换。在本例中,您将其分配给Object类型的变量,在这种情况下不需要强制转换,而且编译器似乎没有插入强制转换。但是,在这种情况下,其他编译器也可以插入强制转换。