猿问

选角时的暧昧行为

我在教学生老式泛型时,遇到了一个看不见的问题!我在演讲时的行为!:(


我有一个简单的课程


public class ObjectUtility {


  public static void main(String[] args) {

    System.out.println(castToType(10,new HashMap<Integer,Integer>()));

  }


  private static <V,T> T castToType(V value, T type){

    return (T) value;

  }


}

这给出的输出为 10,没有任何错误!我期望这会给我一个 ClassCastException,并出现一些错误,例如 Integer Cannot be Cast to HashMap。


好奇又愤怒,我尝试了getClass()返回值,如下所示


System.out.println(castToType(10,new HashMap<Integer,Integer>()).getClass());

正如我所料,它抛出了 ClassCastException 。


另外,当我将同一个语句分成两部分时,就像


Object o = castToType(10,new HashMap<Integer,Integer>());

System.out.println(o.getClass());

它不会抛出任何错误并打印class java.lang.Integer


全部执行


openjdk version "1.7.0_181"

OpenJDK Runtime Environment (Zulu 7.23.0.1-macosx) (build 1.7.0_181-b01)

OpenJDK 64-Bit Server VM (Zulu 7.23.0.1-macosx) (build 24.181-b01, mixed mode)

有人能指出我为什么会发生这种行为的正确方向吗?


慕盖茨4494581
浏览 107回答 2
2回答

海绵宝宝撒

T运行时不存在。它解析为约束的下限。在本例中,没有,因此解析为Object。一切都可以转换为Object,因此没有类转换异常。如果您要将约束更改为此private static <V,T extends Map<?,?>> T castToType(V value, T type){&nbsp; &nbsp; return (T) value;}然后转换为T转换为下界Map,这显然Integer不是,并且您会得到您期望的类转换异常。另外,当我将同一个语句分成两部分时,就像Object o = castToType(10,new HashMap<Integer,Integer>());System.out.println(o.getClass());它没有抛出任何错误castToType(10,new HashMap<Integer,Integer>()).getClass()&nbsp;这会引发类转换异常,因为它静态链接到方法HashMap::getClass(not Object::getClass),因为签名表示期望HashMap作为返回值。这需要隐式转换,HashMap但会失败,因为在运行时castToType返回 an Integer。当你第一次使用这个时Object o = castToType(10,new HashMap<Integer,Integer>());您现在静态链接,Object::getClass无论实际返回什么,都可以。“未分割”版本相当于这个final HashMap<Integer, Integer> map = castToType(10, new HashMap<>());System.out.println(map.getClass());希望能证明差异

catspeake

您可以使用 javap 工具看到差异。编译过程默认进行代码优化,将泛型类型更改为原始类型第一个代码:public class ObjectUtility {&nbsp; public static void main(String[] args) {&nbsp; &nbsp; System.out.println(castToType(10,new java.util.HashMap<Integer,Integer>()));&nbsp; }&nbsp; private static <V,T> T castToType(V value, T type){&nbsp; &nbsp; return (T) value;&nbsp; }}真正的伪代码:Compiled from "ObjectUtility.java"public class ObjectUtility {&nbsp; public ObjectUtility();&nbsp; &nbsp; descriptor: ()V&nbsp; &nbsp; Code:&nbsp; &nbsp; &nbsp; &nbsp;0: aload_0&nbsp; &nbsp; &nbsp; &nbsp;1: invokespecial #1&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Method java/lang/Object."<init>":()V&nbsp; &nbsp; &nbsp; &nbsp;4: return&nbsp; &nbsp; LineNumberTable:&nbsp; &nbsp; &nbsp; line 1: 0&nbsp; public static void main(java.lang.String[]);&nbsp; &nbsp; descriptor: ([Ljava/lang/String;)V&nbsp; &nbsp; Code:&nbsp; &nbsp; &nbsp; &nbsp;0: getstatic&nbsp; &nbsp; &nbsp;#2&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Field java/lang/System.out:Ljava/io/PrintStream;&nbsp; &nbsp; &nbsp; &nbsp;3: bipush&nbsp; &nbsp; &nbsp; &nbsp; 10&nbsp; &nbsp; &nbsp; &nbsp;5: invokestatic&nbsp; #3&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;&nbsp; &nbsp; &nbsp; &nbsp;8: new&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;#4&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // class java/util/HashMap&nbsp; &nbsp; &nbsp; 11: dup&nbsp; &nbsp; &nbsp; 12: invokespecial #5&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Method java/util/HashMap."<init>":()V&nbsp; &nbsp; &nbsp; 15: invokestatic&nbsp; #6&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Method castToType:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;&nbsp; &nbsp; &nbsp; 18: invokevirtual #7&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Method java/io/PrintStream.println:(Ljava/lang/Object;)V&nbsp; &nbsp; &nbsp; 21: return&nbsp; &nbsp; LineNumberTable:&nbsp; &nbsp; &nbsp; line 4: 0&nbsp; &nbsp; &nbsp; line 5: 21&nbsp; private static <V, T> T castToType(V, T);&nbsp; &nbsp; descriptor: (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;&nbsp; &nbsp; Code:&nbsp; &nbsp; &nbsp; &nbsp;0: aload_0&nbsp; &nbsp; &nbsp; &nbsp;1: areturn&nbsp; &nbsp; LineNumberTable:&nbsp; &nbsp; &nbsp; line 8: 0}通用类型的调用更改为对象,并在系统输出中添加 Integer.valueOf。第二个代码:public class ObjectUtility {&nbsp; public static void main(String[] args) {&nbsp; &nbsp; System.out.println(castToType(10,new java.util.HashMap<Integer,Integer>()).getClass());&nbsp; }&nbsp; private static <V,T> T castToType(V value, T type){&nbsp; &nbsp; return (T) value;&nbsp; }}真正的伪代码:Compiled from "ObjectUtility.java"public class ObjectUtility {&nbsp; public ObjectUtility();&nbsp; &nbsp; descriptor: ()V&nbsp; &nbsp; Code:&nbsp; &nbsp; &nbsp; &nbsp;0: aload_0&nbsp; &nbsp; &nbsp; &nbsp;1: invokespecial #1&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Method java/lang/Object."<init>":()V&nbsp; &nbsp; &nbsp; &nbsp;4: return&nbsp; &nbsp; LineNumberTable:&nbsp; &nbsp; &nbsp; line 1: 0&nbsp; public static void main(java.lang.String[]);&nbsp; &nbsp; descriptor: ([Ljava/lang/String;)V&nbsp; &nbsp; Code:&nbsp; &nbsp; &nbsp; &nbsp;0: getstatic&nbsp; &nbsp; &nbsp;#2&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Field java/lang/System.out:Ljava/io/PrintStream;&nbsp; &nbsp; &nbsp; &nbsp;3: bipush&nbsp; &nbsp; &nbsp; &nbsp; 10&nbsp; &nbsp; &nbsp; &nbsp;5: invokestatic&nbsp; #3&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;&nbsp; &nbsp; &nbsp; &nbsp;8: new&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;#4&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // class java/util/HashMap&nbsp; &nbsp; &nbsp; 11: dup&nbsp; &nbsp; &nbsp; 12: invokespecial #5&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Method java/util/HashMap."<init>":()V&nbsp; &nbsp; &nbsp; 15: invokestatic&nbsp; #6&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Method castToType:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;&nbsp; &nbsp; &nbsp; 18: checkcast&nbsp; &nbsp; &nbsp;#4&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // class java/util/HashMap&nbsp; &nbsp; &nbsp; 21: invokevirtual #7&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Method java/lang/Object.getClass:()Ljava/lang/Class;&nbsp; &nbsp; &nbsp; 24: invokevirtual #8&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Method java/io/PrintStream.println:(Ljava/lang/Object;)V&nbsp; &nbsp; &nbsp; 27: return&nbsp; &nbsp; LineNumberTable:&nbsp; &nbsp; &nbsp; line 4: 0&nbsp; &nbsp; &nbsp; line 5: 27&nbsp; private static <V, T> T castToType(V, T);&nbsp; &nbsp; descriptor: (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;&nbsp; &nbsp; Code:&nbsp; &nbsp; &nbsp; &nbsp;0: aload_0&nbsp; &nbsp; &nbsp; &nbsp;1: areturn&nbsp; &nbsp; LineNumberTable:&nbsp; &nbsp; &nbsp; line 8: 0}checkcast 通过 HashMap 调用,但签名更改为 Object,返回值是 int,没有在castToType 中进行强制转换。“int”基元类型导致无效的强制转换第三个代码:public class ObjectUtility {&nbsp; public static void main(String[] args) {&nbsp; &nbsp; Object o = castToType(10,new java.util.HashMap<Integer,Integer>());&nbsp; &nbsp; System.out.println(o.getClass());&nbsp; }&nbsp; private static <V,T> T castToType(V value, T type){&nbsp; &nbsp; return (T) value;&nbsp; }}真正的伪代码:Compiled from "ObjectUtility.java"public class ObjectUtility {&nbsp; public ObjectUtility();&nbsp; &nbsp; descriptor: ()V&nbsp; &nbsp; Code:&nbsp; &nbsp; &nbsp; &nbsp;0: aload_0&nbsp; &nbsp; &nbsp; &nbsp;1: invokespecial #1&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Method java/lang/Object."<init>":()V&nbsp; &nbsp; &nbsp; &nbsp;4: return&nbsp; &nbsp; LineNumberTable:&nbsp; &nbsp; &nbsp; line 1: 0&nbsp; public static void main(java.lang.String[]);&nbsp; &nbsp; descriptor: ([Ljava/lang/String;)V&nbsp; &nbsp; Code:&nbsp; &nbsp; &nbsp; &nbsp;0: bipush&nbsp; &nbsp; &nbsp; &nbsp; 10&nbsp; &nbsp; &nbsp; &nbsp;2: invokestatic&nbsp; #2&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;&nbsp; &nbsp; &nbsp; &nbsp;5: new&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;#3&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // class java/util/HashMap&nbsp; &nbsp; &nbsp; &nbsp;8: dup&nbsp; &nbsp; &nbsp; &nbsp;9: invokespecial #4&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Method java/util/HashMap."<init>":()V&nbsp; &nbsp; &nbsp; 12: invokestatic&nbsp; #5&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Method castToType:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;&nbsp; &nbsp; &nbsp; 15: astore_1&nbsp; &nbsp; &nbsp; 16: getstatic&nbsp; &nbsp; &nbsp;#6&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Field java/lang/System.out:Ljava/io/PrintStream;&nbsp; &nbsp; &nbsp; 19: aload_1&nbsp; &nbsp; &nbsp; 20: invokevirtual #7&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Method java/lang/Object.getClass:()Ljava/lang/Class;&nbsp; &nbsp; &nbsp; 23: invokevirtual #8&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Method java/io/PrintStream.println:(Ljava/lang/Object;)V&nbsp; &nbsp; &nbsp; 26: return&nbsp; &nbsp; LineNumberTable:&nbsp; &nbsp; &nbsp; line 4: 0&nbsp; &nbsp; &nbsp; line 5: 16&nbsp; &nbsp; &nbsp; line 6: 26&nbsp; private static <V, T> T castToType(V, T);&nbsp; &nbsp; descriptor: (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;&nbsp; &nbsp; Code:&nbsp; &nbsp; &nbsp; &nbsp;0: aload_0&nbsp; &nbsp; &nbsp; &nbsp;1: areturn&nbsp; &nbsp; LineNumberTable:&nbsp; &nbsp; &nbsp; line 9: 0}在这种情况下,方法与第一种类似。castToType 返回第一个参数而不做任何更改。正如您所看到的,java 编译器进行了一些“性能”更改,这些更改在某些情况下可能会产生影响。泛型是源代码的“发明”,最终会转换为任何情况下所需的实际类型。
随时随地看视频慕课网APP

相关分类

Java
我要回答