我們都知道,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的,向下轉型則是不被允許的,因為繼承的關係,父類不能保證所有的所有的元素都能強制轉換(元素可以是指定型別的任意子類),所以乾脆杜絕了所有的向下轉型了!(我猜應該是出於效能的考慮)
完。