当前位置: 首页 > 知识库问答 >
问题:

Java泛型转换奇怪[重复]

蓬兴国
2023-03-14

我使用的是Java8。

我最近遇到了这个问题:

public class Test {
    public static void main(String[] args) {
        String ss = "" + (Test.<Integer>abc(2));
        System.out.println(Test.<Integer>abc(2));
    }
    public static <T> T abc(T a) {
        String s = "adsa";
        return (T) s;
    }
}

这不会抛出java.lang.ClassCastException,为什么呢?

我总是想<code>和<code>System.out。println调用到字符串。但当我尝试这样做时,它会像预期的那样抛出异常。

String sss = (Test.<Integer>abc(2)).toString();

共有2个答案

尤俊誉
2023-03-14

泛型在运行时消失,所以对 T 的强制转换实际上只是对 Object 的强制转换(编译器实际上会删除它),所以没有类强制转换异常

abc只是一个获取对象并返回对象的方法<代码>StringBuilder。append(Object)是被调用的方法,从字节码可以看出:

...  
16: invokestatic  #7   // Method abc:(Ljava/lang/Object;)Ljava/lang/Object;
19: invokevirtual #8   // Method java/lang/StringBuilder.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder;
...

当您这样做时

String sss = (Test.<Integer>abc(2)).toString();

那么字节码是

...
 4: invokestatic  #3   // Method abc:(Ljava/lang/Object;)Ljava/lang/Object;
 7: checkcast     #4   // class java/lang/Integer
10: invokevirtual #5   // Method java/lang/Integer.toString:()Ljava/lang/String;
...

您的代码在检查广播操作时失败,这在之前不存在。

宋铭
2023-03-14

它不会抛出< code>ClassCastException,因为所有泛型类型信息都从编译后的代码中去除了(这个过程称为类型擦除)。基本上,任何类型参数都由< code>Object替换。这就是第一个版本有效的原因。这也是代码能够编译的原因。如果您要求编译器用< code>-Xlint:unchecked标志警告未检查或不安全的操作,您将在< code>abc()的< code>return语句中得到有关未检查强制转换的警告。

有了这个声明:

String sss = (Test.<Integer>abc(2)).toString();

故事有点不一样。当类型参数< code>T被< code>Object替换时,调用代码被转换为字节代码,该字节代码将结果显式地转换为< code>Integer。就好像代码是用签名为< code > static Object ABC(Object)的方法编写的,语句是这样写的:

String sss = ((Integer) Test.abc(Integer.valueOf(2))).toString();

也就是说,不仅 abc() 内部的强制转换会因类型擦除而消失,编译器还会在调用代码中插入新的强制转换。此强制转换在运行时生成 ClassCastException,因为从 abc() html" target="_blank">返回的对象是字符串,而不是整数

请注意,该语句

String ss = "" + (Test.<Integer>abc(2));

不需要强制转换,因为编译器只是将< code>abc()返回的对象输入到对象的字符串连接操作中。(具体操作方式因Java编译器而异,但它要么是对< code > StringBuilder append方法的调用,要么是对由< code>StringConcatFactory创建的方法的调用。)这里的细节不重要;关键是编译器足够聪明,能够认识到不需要强制转换。

 类似资料:
  • 问题内容: 我刚刚注意到,Swift在Int和Double上做了一些类型转换。当我尝试评估 是预期的,但实际上是。有人可以解释一下吗? 问题答案: 是的,我也发现这很令人惊讶。同时符合and (和Swift 3)。因此,可以使用 浮点文字 初始化a __ 或使用 整数文字 : (对于其他浮点类型(例如和 )也是如此。) 现在,对于我们所有人而言,(客观)C背景这两个语句可能都出乎了意料之外 将值分

  • 问题内容: 这个问题已经在这里有了答案 : 为什么对泛型的这种使用不会引发运行时或编译时异常? (3个答案) 2年前关闭。 我正在使用Java 8。 我最近遇到了这个问题: 这不会引发java.lang.ClassCastException。这是为什么? 我一直在想和打电话。但是,当我尝试这样做时,它会按预期抛出异常。 问题答案: 它不会抛出,因为所有通用类型信息都已从编译后的代码中剥离(此过程称

  • 我得到了一个编译错误,我不明白原因。 我正在尝试创建一个扩展另一个生成器的生成器,所有这些生成器都使用泛型类型。 问题是一些泛型方法的返回类型是父类,而不是子类,这就阻止了我链接任何子方法。 下面是一个简单的例子: 为什么会出现编译错误?我怎样才能使它按预期工作? 编辑: 由于下面的答案,我通过以下两个更改成功地解决了这个问题 1-我将BuilderChildStatic类generic更改为正常

  • 问题内容: 我有一堂课 具有通用属性 有更好的方法来执行以下操作吗? 即以不同的形式返回泛型? 精度,我使用Gson作为反序列化器,以获取列表,然后每个Data对象都可以是异构的 。还可以注册适配器以进行浮点和长检测,但不会更快或更漂亮 编辑 :gson无法检索多头: 要么: java.lang.Double无法转换为java.lang.Long 要么 java.lang.NumberFormat

  • 我有一个类<代码>数据 具有泛型属性 有没有更好的方法来做以下事情? 即以不同的形式返回泛型类型? Precision,我使用Gson作为反序列化器,以获得一个列表,每个数据对象可以是异构的< br >也可以注册适配器进行浮点和长整型检测,但它不会更快或更好 编辑:gson无法检索longs: 或者: java.lang.Double不能java.lang.Long 或 java.lang.Num

  • 我正试图解决一个泛型问题。我在做一些手工选角,感觉好像我做错了什么。我对使用泛型有点陌生,所以很有可能我在某些方面误用了它们。如能提供指导,将不胜感激。 TLDR: 我有一个带有泛型方法的接口,该泛型方法采用参数。我在一个类中实现了这个接口,但在实现器中,我希望确保是特定类型的(假设,这样我就可以提取一些字段)。我怎么能那样做? 详细信息: 下面有一个接口和一个实现类,其中。 具有以下定义: 而具