【问题标题】:Generics and ClassCastException泛型和 ClassCastException
【发布时间】:2016-03-23 01:40:57
【问题描述】:

给定以下代码:

public class ClassCastTest {

    private static class GenericHolder<T> {
        private T object;

        public GenericHolder(Object object) {
            this.object = (T) object;
            System.out.println(getObject());
        }

        public T getObject() {
            return object;
        }
    }

    public static void main(String[] args) {
        GenericHolder<String> foo = new GenericHolder<>(3l);
        System.out.println(foo.getObject());
    }

}

为什么 Java 在 main-method 的第二行而不是 GenericHolder 的第二行抛出 ClassCastException?

【问题讨论】:

  • 为什么还要选演员?如果这是您所期望的,为什么不简单地将 T 传递给构造函数呢? public GenericHolder(T object) { this.object = object; }
  • 这只是一个例子;)我不需要那个代码

标签: java generics


【解决方案1】:

由于泛型在语言中的实现方式,您对(T) 的强制转换实际上并没有做任何事情。只有当你使用泛型类型时,才能真正得到具体类型——这里,System.out.println 需要一个 String 并且它会进行强制转换来获得它——运行时实际上会进行任何强制转换。

就Java 运行时而言,GenericHolder&lt;String&gt;GenericHolder&lt;Integer&gt; 之间没有区别;他们都持有Object。 Java 只是在您从泛型类型中获得具体类型的任何地方插入强制转换。

研究type erasure了解更多详情。

【讨论】:

  • 但是构造函数中的第二行不是完全一样吗? System.out.printLn 仍然需要一个字符串,不是吗?
  • 是的,所以它变成了System.out.println((String) foo.getObject()),它进行了转换,但转换失败了。
  • 但它只在 main 中抛出异常,而不是在构造函数中 - 所以我从你的回答中得到的是它只尝试在 main 方法中将其转换为 String 并在构造函数中忽略它
  • 正确,因为就构造函数而言,对T 的强制转换没有任何作用; T 基本上总是Object
  • 我刚刚注意到它实际上是 Long 而不是 Integer。我不确定这是不是故意的。
【解决方案2】:

除了 Louis 的回答之外,指出通过编写 (T) 转换为 T 会在 T 有界的情况下“做某事”是有用的。例如,如果你写

private static class GenericHolder<T extends CharSequence>

相反,然后是行

this.object = (T) object;

确实抛出ClassCastException,因为您不能将Long 转换为CharSequence(注意问题是3l 而不是31)。但是,此时它不会尝试转换为 String,因为 T 类型在运行时是未知的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多