• 从字节码看java类型转换【 深入理解 (T[]) new Object[size] 】


      我们都知道,java中对类型的检查是很严格的,所以我们平操作时,也往往很小心。

      如题: (T[]) new Object[size],这种写法是一般我们是不会干的!但是有点经验的同学,还是会遇到这样写的。那么,今天咱们就来看看,像这样的写法对不对,也顺便深入理解java的类型转换机制吧!


    问题1: 如题 (T[]) new Object[size]  的写法对不对?

      答案是肯定的,没毛病。

      为啥呢? 因为 java 的泛型只是语法糖,在java编译后,就不见了,到最后都会转为 object 类型的中间类型,所以,没毛病!


    问题2: 如题所示的变量,能直接使用吗?

      答案待定。不过,我们写的代码应该是不会有什么问题了!如下:

    MyObjClz[] clzArr = getT();    // 直接获取变量,编译不报错

      然后,由于看起来没毛病,我们就可以坑哧坑哧写后续代码了!

      然而事实证明,这是错的!为啥呢? 你应该知道了,这里有类型转换错误!

      好吧,从这里我们得到一个教训,正向没问题的东西,不代表反向也没问题!

      既然整个数组获取回来,会发生类型转换错误,那么我们可以想办法避开这个问题,比如我一个元素一个元素的获取,应该就没问题了吧。因为我们明确知道内部元素的具体类型,而我们只是做了一个 object 的中间转换而已,所以理论正确。比如:

    MyObjClz clz1 = getT()[0];    // 我只获取第一个就行了,因为 整个数组转换已经不OK

      嗯,IDE还是不会报错的,我们又可以坑哧坑哧写代码了。

      糟糕,运行还是异常了!哎,既然都会导致报错,那为嘛要搞这种语法呢?让我们继续看!


    问题3:我们到底怎样才可以使用如题创建的变量?

      其实和我们上面最后一个解题思路是一致的,整个数组类型转换是不可能了,那就单个转呗!不过,这个单个是要从源头开始。即示例如下:

    MyObjClz clz1 = getTOne(i);    // 直接让方法返回 单个元素

      如上,运行妥妥的,我们终于可以安心睡觉了。但是为啥呢?让我们继续!


    问题4:如题所示的语法到底有啥用?

      额,还是很有用的!比如: ArrayList<E>, ArrayQueue<T>, 等等,里面所支持的泛型,最终都会使用到Object 来进行变量保存的,因为既然是泛型,也就是说,在写代码的时候,是不会知道变量类型的,不知道类型自然是保存不了变量的。所以必须使用 Object[] !

    下面来看个应用的例子(可以想像为一个栈队列):

     1 public class ObjectCastToAnother {
     2     public static void main(String[] args) {
     3 
     4         ArrayAGeneric<User> arrayAGeneric = new ArrayAGeneric<>();
     5         arrayAGeneric.push(new User());
     6         arrayAGeneric.push(new User());
     7         // 正确的使用姿势,返回一个元素,直接使用
     8         User us1 = arrayAGeneric.pop();
     9         System.out.println("us1: " + us1);
    10         // 如下是反而教材,这句是会报错的
    11         User us2 = arrayAGeneric.getQueue()[0];
    12         System.out.println("us2: " + us2);
    13     }
    14 }
    15 
    16 class ArrayAGeneric<T> {
    17     private T[] queue;
    18     private int tail = 0;
    19     public ArrayAGeneric() {
    20         System.out.println("gen ok");
    21         queue = newArray(10);
    22     }
    23 
    24     private T[] newArray(int size) {
    25         System.out.println("new array T[]");
    26         return (T[]) new Object[size];
    27     }
    28 
    29     public void push(T u) {
    30         queue[tail++] = u;
    31     }
    32 
    33     public T pop() {
    34         return queue[--tail];
    35     }
    36 
    37     public T[] getQueue() {
    38         return queue;
    39     }
    40 }

      例子一看就懂,就是一个简单的 插入元素,获取元素,使用而已。但是我们的目的是来分析,为什么两种简单的使用,一个会报错,而另一个不会报错,以及 (T[]) new Object[x]为啥不会报错!即如下:

            User us = arrayAGeneric.pop();                // 正确
            User us2 = arrayAGeneric.getQueue()[0];        // 错误
            return (T[]) new Object[size];                // 什么操作?

      看起来差距只在是由谁来取元素的问题了!那么,到底是不是这样呢?(java理论书上肯定有确切的答案)

    那我们换个思路来看问题,然后java代码看不出差别,那么,我们是不是可以换成另一种方式来查看呢?是的,class字节码文件。

    反编译一下,会得到两个文件:

    javap -verbose -p ObjectCastToAnnother.class  # 反编译class

    1. main 文件

      1 Classfile /D:/www/java/target/classes/com/xxx/tester/ObjectCastToAnnother.class
      2   Last modified 2018-11-18; size 1398 bytes
      3   MD5 checksum 8a1815ea41426d67e1a4b68bed4ca914
      4   Compiled from "ObjectCastToAnnother.java"
      5 public class com.xxx.tester.ObjectCastToAnnother
      6   minor version: 0
      7   major version: 52
      8   flags: ACC_PUBLIC, ACC_SUPER
      9 Constant pool:
     10    #1 = Methodref          #20.#41        // java/lang/Object."<init>":()V
     11    #2 = Class              #42            // com/xxx/tester/ArrayAGeneric
     12    #3 = Methodref          #2.#41         // com/xxx/tester/ArrayAGeneric."<init>":()V
     13    #4 = Class              #43            // com/xxx/pojo/user/User
     14    #5 = Methodref          #4.#41         // com/xxx/pojo/user/User."<init>":()V
     15    #6 = Methodref          #2.#44         // com/xxx/tester/ArrayAGeneric.push:(Ljava/lang/Object;)V
     16    #7 = Methodref          #2.#45         // com/xxx/tester/ArrayAGeneric.pop:()Ljava/lang/Object;
     17    #8 = Fieldref           #46.#47        // java/lang/System.out:Ljava/io/PrintStream;
     18    #9 = Class              #48            // java/lang/StringBuilder
     19   #10 = Methodref          #9.#41         // java/lang/StringBuilder."<init>":()V
     20   #11 = String             #49            // us1:
     21   #12 = Methodref          #9.#50         // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
     22   #13 = Methodref          #9.#51         // java/lang/StringBuilder.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder;
     23   #14 = Methodref          #9.#52         // java/lang/StringBuilder.toString:()Ljava/lang/String;
     24   #15 = Methodref          #53.#54        // java/io/PrintStream.println:(Ljava/lang/String;)V
     25   #16 = Methodref          #2.#55         // com/xxx/tester/ArrayAGeneric.getQueue:()[Ljava/lang/Object;
     26   #17 = Class              #56            // "[Lcom/xxx/pojo/user/User;"
     27   #18 = String             #57            // us2:
     28   #19 = Class              #58            // com/xxx/tester/ObjectCastToAnnother
     29   #20 = Class              #59            // java/lang/Object
     30   #21 = Utf8               <init>
     31   #22 = Utf8               ()V
     32   #23 = Utf8               Code
     33   #24 = Utf8               LineNumberTable
     34   #25 = Utf8               LocalVariableTable
     35   #26 = Utf8               this
     36   #27 = Utf8               Lcom/xxx/tester/ObjectCastToAnnother;
     37   #28 = Utf8               main
     38   #29 = Utf8               ([Ljava/lang/String;)V
     39   #30 = Utf8               args
     40   #31 = Utf8               [Ljava/lang/String;
     41   #32 = Utf8               arrayAGeneric
     42   #33 = Utf8               Lcom/xxx/tester/ArrayAGeneric;
     43   #34 = Utf8               us
     44   #35 = Utf8               Lcom/xxx/pojo/user/User;
     45   #36 = Utf8               us2
     46   #37 = Utf8               LocalVariableTypeTable
     47   #38 = Utf8               Lcom/xxx/tester/ArrayAGeneric<Lcom/xxx/pojo/user/User;>;
     48   #39 = Utf8               SourceFile
     49   #40 = Utf8               ObjectCastToAnnother.java
     50   #41 = NameAndType        #21:#22        // "<init>":()V
     51   #42 = Utf8               com/xxx/tester/ArrayAGeneric
     52   #43 = Utf8               com/xxx/pojo/user/User
     53   #44 = NameAndType        #60:#61        // push:(Ljava/lang/Object;)V
     54   #45 = NameAndType        #62:#63        // pop:()Ljava/lang/Object;
     55   #46 = Class              #64            // java/lang/System
     56   #47 = NameAndType        #65:#66        // out:Ljava/io/PrintStream;
     57   #48 = Utf8               java/lang/StringBuilder
     58   #49 = Utf8               us1:
     59   #50 = NameAndType        #67:#68        // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
     60   #51 = NameAndType        #67:#69        // append:(Ljava/lang/Object;)Ljava/lang/StringBuilder;
     61   #52 = NameAndType        #70:#71        // toString:()Ljava/lang/String;
     62   #53 = Class              #72            // java/io/PrintStream
     63   #54 = NameAndType        #73:#74        // println:(Ljava/lang/String;)V
     64   #55 = NameAndType        #75:#76        // getQueue:()[Ljava/lang/Object;
     65   #56 = Utf8               [Lcom/xxx/pojo/user/User;
     66   #57 = Utf8               us2:
     67   #58 = Utf8               com/xxx/tester/ObjectCastToAnnother
     68   #59 = Utf8               java/lang/Object
     69   #60 = Utf8               push
     70   #61 = Utf8               (Ljava/lang/Object;)V
     71   #62 = Utf8               pop
     72   #63 = Utf8               ()Ljava/lang/Object;
     73   #64 = Utf8               java/lang/System
     74   #65 = Utf8               out
     75   #66 = Utf8               Ljava/io/PrintStream;
     76   #67 = Utf8               append
     77   #68 = Utf8               (Ljava/lang/String;)Ljava/lang/StringBuilder;
     78   #69 = Utf8               (Ljava/lang/Object;)Ljava/lang/StringBuilder;
     79   #70 = Utf8               toString
     80   #71 = Utf8               ()Ljava/lang/String;
     81   #72 = Utf8               java/io/PrintStream
     82   #73 = Utf8               println
     83   #74 = Utf8               (Ljava/lang/String;)V
     84   #75 = Utf8               getQueue
     85   #76 = Utf8               ()[Ljava/lang/Object;
     86 {
     87   public com.xxx.tester.ObjectCastToAnnother();
     88     descriptor: ()V
     89     flags: ACC_PUBLIC
     90     Code:
     91       stack=1, locals=1, args_size=1
     92          0: aload_0
     93          1: invokespecial #1                  // Method java/lang/Object."<init>":()V
     94          4: return
     95       LineNumberTable:
     96         line 8: 0
     97       LocalVariableTable:
     98         Start  Length  Slot  Name   Signature
     99             0       5     0  this   Lcom/xxx/tester/ObjectCastToAnnother;
    100 
    101   public static void main(java.lang.String[]);
    102     descriptor: ([Ljava/lang/String;)V
    103     flags: ACC_PUBLIC, ACC_STATIC
    104     Code:
    105       stack=3, locals=4, args_size=1
    106          0: new           #2                  // class com/xxx/tester/ArrayAGeneric
    107          3: dup
    108          4: invokespecial #3                  // Method com/xxx/tester/ArrayAGeneric."<init>":()V
    109          7: astore_1
    110          8: aload_1
    111          9: new           #4                  // class com/xxx/pojo/user/User
    112         12: dup
    113         13: invokespecial #5                  // Method com/xxx/pojo/user/User."<init>":()V
    114         16: invokevirtual #6                  // Method com/xxx/tester/ArrayAGeneric.push:(Ljava/lang/Object;)V
    115         19: aload_1
    116         20: new           #4                  // class com/xxx/pojo/user/User
    117         23: dup
    118         24: invokespecial #5                  // Method com/xxx/pojo/user/User."<init>":()V
    119         27: invokevirtual #6                  // Method com/xxx/tester/ArrayAGeneric.push:(Ljava/lang/Object;)V
    120         30: aload_1
    121         31: invokevirtual #7                  // Method com/xxx/tester/ArrayAGeneric.pop:()Ljava/lang/Object;
    122         34: checkcast     #4                  // class com/xxx/pojo/user/User
    123         37: astore_2
    124         38: getstatic     #8                  // Field java/lang/System.out:Ljava/io/PrintStream;
    125         41: new           #9                  // class java/lang/StringBuilder
    126         44: dup
    127         45: invokespecial #10                 // Method java/lang/StringBuilder."<init>":()V
    128         48: ldc           #11                 // String us1:
    129         50: invokevirtual #12                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
    130         53: aload_2
    131         54: invokevirtual #13                 // Method java/lang/StringBuilder.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder;
    132         57: invokevirtual #14                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
    133         60: invokevirtual #15                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
    134         63: aload_1
    135         64: invokevirtual #16                 // Method com/xxx/tester/ArrayAGeneric.getQueue:()[Ljava/lang/Object;
    136         67: checkcast     #17                 // class "[Lcom/xxx/pojo/user/User;"
    137         70: iconst_0
    138         71: aaload
    139         72: astore_3
    140         73: getstatic     #8                  // Field java/lang/System.out:Ljava/io/PrintStream;
    141         76: new           #9                  // class java/lang/StringBuilder
    142         79: dup
    143         80: invokespecial #10                 // Method java/lang/StringBuilder."<init>":()V
    144         83: ldc           #18                 // String us2:
    145         85: invokevirtual #12                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
    146         88: aload_3
    147         89: invokevirtual #13                 // Method java/lang/StringBuilder.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder;
    148         92: invokevirtual #14                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
    149         95: invokevirtual #15                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
    150         98: return
    151       LineNumberTable:
    152         line 11: 0
    153         line 12: 8
    154         line 13: 19
    155         line 14: 30
    156         line 15: 38
    157         line 16: 63
    158         line 17: 73
    159         line 18: 98
    160       LocalVariableTable:
    161         Start  Length  Slot  Name   Signature
    162             0      99     0  args   [Ljava/lang/String;
    163             8      91     1 arrayAGeneric   Lcom/xxx/tester/ArrayAGeneric;
    164            38      61     2    us   Lcom/xxx/pojo/user/User;
    165            73      26     3   us2   Lcom/xxx/pojo/user/User;
    166       LocalVariableTypeTable:
    167         Start  Length  Slot  Name   Signature
    168             8      91     1 arrayAGeneric   Lcom/xxx/tester/ArrayAGeneric<Lcom/xxx/pojo/user/User;>;
    169 }
    170 SourceFile: "ObjectCastToAnnother.java"

     2. ArrayAGeneric 文件

      1 Classfile /D:/www/java/target/classes/com/xxx/tester/ArrayAGeneric.class
      2   Last modified 2018-11-18; size 1390 bytes
      3   MD5 checksum fc9f7f9311bf542d9f1b03e39e32aba8
      4   Compiled from "ObjectCastToAnnother.java"
      5 class com.xxx.tester.ArrayAGeneric<T extends java.lang.Object> extends java.lang.Object
      6   minor version: 0
      7   major version: 52
      8   flags: ACC_SUPER
      9 Constant pool:
     10    #1 = Methodref          #9.#46         // java/lang/Object."<init>":()V
     11    #2 = Fieldref           #11.#47        // com/xxx/tester/ArrayAGeneric.tail:I
     12    #3 = Fieldref           #48.#49        // java/lang/System.out:Ljava/io/PrintStream;
     13    #4 = String             #50            // gen ok
     14    #5 = Methodref          #51.#52        // java/io/PrintStream.println:(Ljava/lang/String;)V
     15    #6 = Methodref          #11.#53        // com/xxx/tester/ArrayAGeneric.newArray:(I)[Ljava/lang/Object;
     16    #7 = Fieldref           #11.#54        // com/xxx/tester/ArrayAGeneric.queue:[Ljava/lang/Object;
     17    #8 = String             #55            // new array T[]
     18    #9 = Class              #56            // java/lang/Object
     19   #10 = Class              #13            // "[Ljava/lang/Object;"
     20   #11 = Class              #57            // com/xxx/tester/ArrayAGeneric
     21   #12 = Utf8               queue
     22   #13 = Utf8               [Ljava/lang/Object;
     23   #14 = Utf8               Signature
     24   #15 = Utf8               [TT;
     25   #16 = Utf8               tail
     26   #17 = Utf8               I
     27   #18 = Utf8               <init>
     28   #19 = Utf8               ()V
     29   #20 = Utf8               Code
     30   #21 = Utf8               LineNumberTable
     31   #22 = Utf8               LocalVariableTable
     32   #23 = Utf8               this
     33   #24 = Utf8               Lcom/xxx/tester/ArrayAGeneric;
     34   #25 = Utf8               LocalVariableTypeTable
     35   #26 = Utf8               Lcom/xxx/tester/ArrayAGeneric<TT;>;
     36   #27 = Utf8               newArray
     37   #28 = Utf8               (I)[Ljava/lang/Object;
     38   #29 = Utf8               size
     39   #30 = Utf8               (I)[TT;
     40   #31 = Utf8               push
     41   #32 = Utf8               (Ljava/lang/Object;)V
     42   #33 = Utf8               u
     43   #34 = Utf8               Ljava/lang/Object;
     44   #35 = Utf8               TT;
     45   #36 = Utf8               (TT;)V
     46   #37 = Utf8               pop
     47   #38 = Utf8               ()Ljava/lang/Object;
     48   #39 = Utf8               ()TT;
     49   #40 = Utf8               getQueue
     50   #41 = Utf8               ()[Ljava/lang/Object;
     51   #42 = Utf8               ()[TT;
     52   #43 = Utf8               <T:Ljava/lang/Object;>Ljava/lang/Object;
     53   #44 = Utf8               SourceFile
     54   #45 = Utf8               ObjectCastToAnnother.java
     55   #46 = NameAndType        #18:#19        // "<init>":()V
     56   #47 = NameAndType        #16:#17        // tail:I
     57   #48 = Class              #58            // java/lang/System
     58   #49 = NameAndType        #59:#60        // out:Ljava/io/PrintStream;
     59   #50 = Utf8               gen ok
     60   #51 = Class              #61            // java/io/PrintStream
     61   #52 = NameAndType        #62:#63        // println:(Ljava/lang/String;)V
     62   #53 = NameAndType        #27:#28        // newArray:(I)[Ljava/lang/Object;
     63   #54 = NameAndType        #12:#13        // queue:[Ljava/lang/Object;
     64   #55 = Utf8               new array T[]
     65   #56 = Utf8               java/lang/Object
     66   #57 = Utf8               com/xxx/tester/ArrayAGeneric
     67   #58 = Utf8               java/lang/System
     68   #59 = Utf8               out
     69   #60 = Utf8               Ljava/io/PrintStream;
     70   #61 = Utf8               java/io/PrintStream
     71   #62 = Utf8               println
     72   #63 = Utf8               (Ljava/lang/String;)V
     73 {
     74   private T[] queue;
     75     descriptor: [Ljava/lang/Object;
     76     flags: ACC_PRIVATE
     77     Signature: #15                          // [TT;
     78 
     79   private int tail;
     80     descriptor: I
     81     flags: ACC_PRIVATE
     82 
     83   public com.xxx.tester.ArrayAGeneric();
     84     descriptor: ()V
     85     flags: ACC_PUBLIC
     86     Code:
     87       stack=3, locals=1, args_size=1
     88          0: aload_0
     89          1: invokespecial #1                  // Method java/lang/Object."<init>":()V
     90          4: aload_0
     91          5: iconst_0
     92          6: putfield      #2                  // Field tail:I
     93          9: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
     94         12: ldc           #4                  // String gen ok
     95         14: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
     96         17: aload_0
     97         18: aload_0
     98         19: bipush        10
     99         21: invokespecial #6                  // Method newArray:(I)[Ljava/lang/Object;
    100         24: putfield      #7                  // Field queue:[Ljava/lang/Object;
    101         27: return
    102       LineNumberTable:
    103         line 24: 0
    104         line 23: 4
    105         line 25: 9
    106         line 26: 17
    107         line 27: 27
    108       LocalVariableTable:
    109         Start  Length  Slot  Name   Signature
    110             0      28     0  this   Lcom/xxx/tester/ArrayAGeneric;
    111       LocalVariableTypeTable:
    112         Start  Length  Slot  Name   Signature
    113             0      28     0  this   Lcom/xxx/tester/ArrayAGeneric<TT;>;
    114 
    115   private T[] newArray(int);
    116     descriptor: (I)[Ljava/lang/Object;
    117     flags: ACC_PRIVATE
    118     Code:
    119       stack=2, locals=2, args_size=2
    120          0: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
    121          3: ldc           #8                  // String new array T[]
    122          5: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
    123          8: iload_1
    124          9: anewarray     #9                  // class java/lang/Object
    125         12: checkcast     #10                 // class "[Ljava/lang/Object;"
    126         15: areturn
    127       LineNumberTable:
    128         line 30: 0
    129         line 31: 8
    130       LocalVariableTable:
    131         Start  Length  Slot  Name   Signature
    132             0      16     0  this   Lcom/xxx/tester/ArrayAGeneric;
    133             0      16     1  size   I
    134       LocalVariableTypeTable:
    135         Start  Length  Slot  Name   Signature
    136             0      16     0  this   Lcom/xxx/tester/ArrayAGeneric<TT;>;
    137     Signature: #30                          // (I)[TT;
    138 
    139   public void push(T);
    140     descriptor: (Ljava/lang/Object;)V
    141     flags: ACC_PUBLIC
    142     Code:
    143       stack=5, locals=2, args_size=2
    144          0: aload_0
    145          1: getfield      #7                  // Field queue:[Ljava/lang/Object;
    146          4: aload_0
    147          5: dup
    148          6: getfield      #2                  // Field tail:I
    149          9: dup_x1
    150         10: iconst_1
    151         11: iadd
    152         12: putfield      #2                  // Field tail:I
    153         15: aload_1
    154         16: aastore
    155         17: return
    156       LineNumberTable:
    157         line 35: 0
    158         line 36: 17
    159       LocalVariableTable:
    160         Start  Length  Slot  Name   Signature
    161             0      18     0  this   Lcom/xxx/tester/ArrayAGeneric;
    162             0      18     1     u   Ljava/lang/Object;
    163       LocalVariableTypeTable:
    164         Start  Length  Slot  Name   Signature
    165             0      18     0  this   Lcom/xxx/tester/ArrayAGeneric<TT;>;
    166             0      18     1     u   TT;
    167     Signature: #36                          // (TT;)V
    168 
    169   public T pop();
    170     descriptor: ()Ljava/lang/Object;
    171     flags: ACC_PUBLIC
    172     Code:
    173       stack=4, locals=1, args_size=1
    174          0: aload_0
    175          1: getfield      #7                  // Field queue:[Ljava/lang/Object;
    176          4: aload_0
    177          5: dup
    178          6: getfield      #2                  // Field tail:I
    179          9: iconst_1
    180         10: isub
    181         11: dup_x1
    182         12: putfield      #2                  // Field tail:I
    183         15: aaload
    184         16: areturn
    185       LineNumberTable:
    186         line 39: 0
    187       LocalVariableTable:
    188         Start  Length  Slot  Name   Signature
    189             0      17     0  this   Lcom/xxx/tester/ArrayAGeneric;
    190       LocalVariableTypeTable:
    191         Start  Length  Slot  Name   Signature
    192             0      17     0  this   Lcom/xxx/tester/ArrayAGeneric<TT;>;
    193     Signature: #39                          // ()TT;
    194 
    195   public T[] getQueue();
    196     descriptor: ()[Ljava/lang/Object;
    197     flags: ACC_PUBLIC
    198     Code:
    199       stack=1, locals=1, args_size=1
    200          0: aload_0
    201          1: getfield      #7                  // Field queue:[Ljava/lang/Object;
    202          4: areturn
    203       LineNumberTable:
    204         line 43: 0
    205       LocalVariableTable:
    206         Start  Length  Slot  Name   Signature
    207             0       5     0  this   Lcom/xxx/tester/ArrayAGeneric;
    208       LocalVariableTypeTable:
    209         Start  Length  Slot  Name   Signature
    210             0       5     0  this   Lcom/xxx/tester/ArrayAGeneric<TT;>;
    211     Signature: #42                          // ()[TT;
    212 }
    213 Signature: #43                          // <T:Ljava/lang/Object;>Ljava/lang/Object;
    214 SourceFile: "ObjectCastToAnnother.java"

    其实从 main 文件中已经看出端倪,第120~123行,即 us1 赋值的地方:

            30: aload_1
            31: invokevirtual #7                  // Method com/xxx/tester/ArrayAGeneric.pop:()Ljava/lang/Object;
            34: checkcast     #4                  // class com/xxx/pojo/user/User
            37: astore_2

    这里看到,有一个 checkcast 的指令,即是进行类型转换检查,而本身的 pop() 后的元素类型一致,因此运行OK!

    我们来看下一取值方式,第134~138行,即 us2 赋值的地方:

            63: aload_1
            64: invokevirtual #16                 // Method com/xxx/tester/ArrayAGeneric.getQueue:()[Ljava/lang/Object;
            67: checkcast     #17                 // class "[Lcom/xxx/pojo/user/User;"
            70: iconst_0
            71: aaload

       看到了吧,关键的地方: checkcast class "[Lcom/xxx/pojo/user/User", 即将获取到的值进行 数组类型的转换检查,如此检查,自然是通不过的了。所以,理解了吧,是因为,数组元素的获取顺序为先进行类型转换,然后再获取元素值!

      现在,还剩下一个问题: 为什么通过 getOne() 的形式,代码就是可行的呢?

      这个问题的答案,在 ArrayAGeneric 的文件中,可以轻松找到答案:

    ArrayAGeneric 文件,第 174~184行:

             0: aload_0
             1: getfield      #7                  // Field queue:[Ljava/lang/Object;
             4: aload_0
             5: dup
             6: getfield      #2                  // Field tail:I
             9: iconst_1
            10: isub
            11: dup_x1
            12: putfield      #2                  // Field tail:I
            15: aaload
            16: areturn

      可以看出来,这里就只是一个数组元素的获取过程,返回类型为 Object, 而此 Object 的原始类型即是泛型指定的。因此,在外部进行转换自然也不会错!

    好了,到此,疑问已经得到回答。是类型转换的检查时机导致了我们的代码错误。

    另外,我们还可以继续看一下  newArray() 的代码:

             0: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
             3: ldc           #8                  // String new array T[]
             5: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
             8: iload_1
             9: anewarray     #9                  // class java/lang/Object
            12: checkcast     #10                 // class "[Ljava/lang/Object;"
            15: areturn

      这里也可以明显的看出, T[] 其实就是 Object[] 。

          数组类型的强转要特别注意,其向上转型是ok的,向下转型则是不被允许的,因为继承的关系,父类不能保证所有的所有的元素都能强制转换(元素可以是指定类型的任意子类),所以干脆杜绝了所有的向下转型了!(我猜应该是出于性能的考虑)

      不要以为ArrayList<T>中的T是指的具体泛型类,完。

  • 相关阅读:
    pureftp 超时 mlsd
    安卓相机调用适配
    解决多个界面重复共用同一组数据导致数据同步改变(实现数据的完全深拷贝)
    PHP反射API的使用、体会、说明
    c冒泡排序
    iOS开发常用的第三方类库
    修改按钮上图片的大小-iOS
    获取手机通讯录--ios
    根据搜素的字符串改变label包含该字符串的文字
    cell点击按钮崩的一种情况
  • 原文地址:https://www.cnblogs.com/yougewe/p/9978452.html
Copyright © 2020-2023  润新知