Java泛型类型方法返回错误的类型

时间:2017-10-23 17:23:41

标签: java generics casting

每隔一段时间,我觉得我根本不懂Java ..我最近发现了Java类型转换的奇怪行为:

public static void main(String[] args) {
    String res = get();
    System.out.println(res);
}

public static <T> T get() {
    Object longObj = Long.valueOf("0");
    T casted = (T) longObj;
    System.out.println("longObj=" + longObj.getClass());
    System.out.println("casted=" + casted.getClass()); // <-- Why the type of "casted" is Long instead of String???
    return casted;
}

输出结果为:

longObj=class java.lang.Long
casted=class java.lang.Long
Exception in thread "main" java.lang.ClassCastException: java.lang.Long 
cannot be cast to java.lang.String
    at com.apple.geo.coreloc.Test.main(Test.java:5)

我的困惑是:为什么变量casted的类型为Long?是不是应该输入T,即String

以下是为什么我认为它应该是String

  • 在运行时,String res = get()提示get()方法返回String
  • T casted = (T) longObj;应该尝试将强制类型转换为字符串(我希望这里有一个例外,但是没有&#39; t ..)

4 个答案:

答案 0 :(得分:3)

泛型是编译时间检查。它们不是编写坏演员的保障措施。

您尝试(String) longIbj;,其中longObj很长。那会给你一个运行时ClassCastException

但是,因为在T get()的上下文中没有编译时错误,所以T get()被正确编译。由于&#34;类型擦除&#34; T被提升为最高约束类型。您没有限制类型的上限,因此编译器处理的代码大致如下:

public static Object get() {
    Object longObj = Long.valueOf("0");
    Object casted = (Object) longObj;
    System.out.println("longObj=" + longObj.getClass());
    System.out.println("casted=" + casted.getClass()); // <-- Why the type of "casted" is Long instead of String???
    return casted;
}

由于<T>Object,填充longObjcasted的两行代码大致相同。在任何情况下,通过名称对对象引用的多态调用仍将解析为对象的.getClass()方法,该方法仍将返回java.lang.Long

使用Java进行强制转换不会创建新对象。如果您想要一个不同的对象,则需要new一个不同的对象并用相关信息填充它。使用java进行强制转换只能通过强制转换类型的方法名访问该类。因此,getLong()投放Long可能会失去Object,但它仍然存在(如果您通过Long类型的变量调用它。

答案 1 :(得分:2)

投射与转化不同。

Casting是一个声明,您的对象IS是目标类型。它不会改变当前对象的类。

转换是将一种类型的对象重新创建为另一种类型的对象的过程。

这就是为什么只有在您已经知道要投射的对象的类型时才应该使用。在所有其他情况下,您应该将对象转换为目标类型的实例。

答案 2 :(得分:1)

在Java中,泛型类型仅在编译时起作用,但它(几乎)没有关于特定目标泛型类型的信息,因为所谓的“类型擦除”(参见herehere)。

如果您反编译代码,您会看到:

public static void main(String[] args) {
    String res = (String)get();
    System.out.println(res);
}

public static <T> T get() {
    Object longObj = Long.valueOf("123");
    System.out.println("longObj=" + longObj.getClass());
    System.out.println("casted=" + longObj.getClass());
    return longObj;
}

这一切都很清楚:

  • main中,它只是强制转换操作
  • get中,带有“已投放”值的变量已完全删除

答案 3 :(得分:1)

Java泛型与C ++模板不同。对于Java泛型方法,该方法只有一个版本。在C ++中,根据特定类型的需要生成模板化方法的代码,以便每个不同的参数化类型获得它自己的方法版本。

要获得您期望的错误,您必须通过告诉它通用类型来帮助Java。

class A {
  public static void main(String[] args) {
    String res = get(String.class);
    System.out.println(res);
  }

  public static <T> T get( Class<T> klass) {

    Object longObj = Long.valueOf("0");
    T casted = klass.cast(longObj);
    System.out.println("longObj=" + longObj.getClass());
    System.out.println("casted=" + casted.getClass());
    return casted;
  }
}

运行此操作会产生强制转换异常:

Exception in thread "main" java.lang.ClassCastException: Cannot cast java.lang.Long to java.lang.String
    at java.lang.Class.cast(Class.java:3369)
    at A.get(A.java:10)
    at A.main(A.java:4)