我们都知道,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[] 。
其实只要再深入一点,就不致被表面现象迷惑!