java泛型的类型是在编译时决定的吗?为什么我们可以在运行时更改类型?

java泛型的类型是在编译时决定的吗?为什么我们可以在运行时更改类型?,java,generics,Java,Generics,这段代码的输出对您有意义吗?它给出以下输出: class java.lang.String class java.lang.Integer class java.lang.String 因此,在启动测试对象时,类型似乎被分配给Integer,但成员的类型在运行时发生了变化。为什么会发生这种情况 public class Test { public static void main(String[] args) { MyType<Integer> test =

这段代码的输出对您有意义吗?它给出以下输出:

class java.lang.String
class java.lang.Integer
class java.lang.String
因此,在启动测试对象时,类型似乎被分配给Integer,但成员的类型在运行时发生了变化。为什么会发生这种情况

public class Test {
    public static void main(String[] args) {
        MyType<Integer> test = new MyType<Integer>("hello", 1, "world");
        }

    public static class MyType<T> {
        private T member;

        public MyType(Object o, Object o2, Object o3) {
            T t = (T) o;
            member = (T) o2;
            System.out.println(t.getClass());
            System.out.println(member.getClass());
            member = (T) o3;
            System.out.println(member.getClass());
        }
    }
}
公共类测试{
公共静态void main(字符串[]args){
MyType测试=新的MyType(“你好”,1,“世界”);
}
公共静态类MyType{
私人T会员;
公共MyType(对象o、对象o2、对象o3){
T=(T)o;
成员=(T)o2;
System.out.println(t.getClass());
System.out.println(member.getClass());
成员=(T)o3;
System.out.println(member.getClass());
}
}
}

它不会更改,您可以将对象强制转换为其他类型,但引用是相同的。您的代码是错误的,因为您将对象向上转换为错误的类型。但由于引用是指向原始对象的,因此getClass将返回实际对象的类型。
如果使用这些变量,可能会使应用程序崩溃。

这种看似奇怪的行为是由于java如何使用类型擦除实现泛型。您可以查看有关类型擦除的更详细解释,但我可以在此场景中总结其效果

打电话的时候

 new MyType<Integer>("hello", 1, "world");
然而,在运行时,情况并非如此——使用类型擦除,在编译时,泛型中使用的类型被“擦除”,而内部类代码被编译为:

public static class MyType {
    private Object member;

    public MyType(Object o, Object o2, Object o3) {
        Object t = o;
        member = o2;
        System.out.println(t.getClass());
        System.out.println(member.getClass());
        member = o3;
        System.out.println(member.getClass());
    }
}
因此,在这一点上,对象类型被指定给不会导致任何错误的对象类型-尽管实际引用的对象是不同类型的(o2是一个int,o3是一个字符串)。这就是为什么member.getClass()返回引用的实际类(在本例中为整数和字符串)

那么泛型参数是什么时候

<Integer>
调用test.getMember().doubleValue()时,编译器会尝试将其强制转换为整数泛型参数,但您将其设置为(“world”)的参数是字符串,因此会引发ClassCastException。编译后的调用如下所示:

System.out.println(((Integer) test.getMember()).doubleValue());
请注意,如果“member”是公共的,并且我跳过了使用getter(如test.member.doubleValue()中),则会发生同样的情况


因此,本质上你的ClassCastException被延迟了,因为类型被删除了。此服务器上的Oracle文档可用作以下资源:。如果有人知道得更清楚,请在必要时纠正我对类型擦除的解释。谢谢。

您希望得到什么样的输出?另外,为什么您会问“为什么我们可以在运行时更改类型?”您给了该方法一个
字符串、一个
整数和另一个
字符串,那么类型“更改”在哪里呢。这正是输出。将更易于维护和优雅,以使方法通用并减少来回转换的开销。我认为您缺少类型擦除的概念:如果打开所有编译器警告,您将看到您的转换在语法上是允许的,但实际上是无效的。“如果使用这些变量,应用程序可能会崩溃。“但他们正在使用它们。为什么不提供一个修改过的会崩溃的例子呢?我不能,不仅因为我没有java,还因为ctash随机。基本上,您是否强制编译器接受一个事实,即字符串是一个整数,而您没有Java?给你,不要说我从来没有给过你任何东西:而且类型崩溃不会是随机的。如果你能编辑我给你的片段,让它产生,那么它将是100%可复制的。布兰登·赖特解释说,你是对的,它们不是随机的。感谢Brandon Wright提供的详细答案。早些时候,我还尝试将“member”公开,并使用test.member=“hello”访问它。这引发了编译时错误,如您所述。如果我理解正确,泛型只会在类外调用属性而不是在类内(在构造函数或属于“MyType”类的方法内)时进行类型检查。我想这句话进一步证实了这一点:构造函数的类型,实例方法,或未从其超类或超接口继承的原始类型C的非静态字段是对应于C的泛型声明中删除其类型的原始类型。
public class Test {
    public static void main(String[] args) {
        MyType<Integer> test = new MyType<Integer>("hello", 1, "world");
        // attempting to use the member variable as an Integer
        System.out.println(test.getMember().doubleValue());
        }

    public static class MyType<T> {
        private T member;

        public MyType(Object o, Object o2, Object o3) {
            T t = (T) o;
            member = (T) o2;
            System.out.println(t.getClass());
            System.out.println(member.getClass());
            member = (T) o3;
            System.out.println(member.getClass());
        }

        public T getMember() {
            return member;
        }
    }
}
Exception in thread "main" java.lang.ClassCastException: 
    java.lang.String cannot be cast to java.lang.Integer
System.out.println(((Integer) test.getMember()).doubleValue());