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

栏目: Java · 发布时间: 7年前

内容简介:我们都知道,java中对类型的检查是很严格的,所以我们平操作时,也往往很小心。如题: (T[]) new Object[size],这种写法是一般我们是不会干的!但是有点经验的同学,还是会遇到这样写的。那么,今天咱们就来看看,像这样的写法对不对,也顺便深入理解java的类型转换机制吧!问题1: 如题 (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 us = arrayAGeneric.pop();
 9         System.out.println("us1: " + us);
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[] 。

其实只要再深入一点,就不致被表面现象迷惑!


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Kotlin实战

Kotlin实战

【美】Dmitry Jemerov(德米特里·詹莫瑞福)、【美】 Svetlana Isakova(斯维特拉娜·伊凡诺沃) / 覃宇、罗丽、李思阳、蒋扬海 / 电子工业出版社 / 2017-8 / 89.00

《Kotlin 实战》将从语言的基本特性开始,逐渐覆盖其更多的高级特性,尤其注重讲解如何将 Koltin 集成到已有 Java 工程实践及其背后的原理。本书分为两个部分。第一部分讲解如何开始使用 Kotlin 现有的库和API,包括基本语法、扩展函数和扩展属性、数据类和伴生对象、lambda 表达式,以及数据类型系统(着重讲解了可空性和集合的概念)。第二部分教你如何使用 Kotlin 构建自己的 ......一起来看看 《Kotlin实战》 这本书的介绍吧!

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具