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

时间:2017-02-22 03:04:29

标签: 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 = 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());
        }
    }
}

2 个答案:

答案 0 :(得分:4)

看似奇怪的行为是由于java如何使用类型擦除来实现泛型。您可以查看this question以获取有关类型擦除的更详细说明,但我可以在此方案中总结它的效果。

致电

 new MyType<Integer>("hello", 1, "world");

看起来构造函数试图同时转换int类型&#34; 1&#34;和字符串类型&#34;世界&#34;到&#34;会员&#34;实例变量,其类型为Integer:

        member = (T) o2;
        System.out.println(t.getClass());
        System.out.println(member.getClass());
        member = (T) o3;

然而,在运行时,这不是正在发生的事情 - 在编译时,类型擦除,泛型中使用的类型被删除&#34;并且内部类代码被编译为:

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());
    }
}

所以此时Object类型被分配给Object类型,不会导致任何错误 - 尽管实际引用的对象是不同的类型(o2是int,o3是String)。这就是为什么member.getClass()返回引用的实际类(在本例中为Integer和String)。

那么通用参数何时是

<Integer>
实际上是发挥了作用?当它被使用。我对您的代码进行了一些小修改,尝试访问成员字段并在其上调用Integer方法:

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

当test.getMember()。doubleValue()被调用时,编译器会尝试将其强制转换为您的Integer泛型参数 - 但是您将其设置为(&#34; world&#34;)的参数是一个String抛出ClassCastException。编译后的调用看起来像这样:

System.out.println(((Integer) test.getMember()).doubleValue());

请注意,如果&#34;成员&#34;会发生同样的事情。是公开的,我跳过使用getter(如test.member.doubleValue())。

所以基本上你的ClassCastException被延迟了,因为类型被删除了。关于此的Oracle文档作为资源非常有用:https://docs.oracle.com/javase/tutorial/java/generics/genMethods.html。如果有人知道更好,请在必要时纠正我对类型擦除的解释。感谢。

答案 1 :(得分:-1)

它没有改变,你将对象转换为另一种类型,但引用是相同的。在将对象转换为错误类型的意义上,您的代码是错误的。但由于referance是原始对象,getClass将返回真实对象的类型。 如果您使用这些变量,则可能会导致应用程序崩溃。