public T[][] getArrayOfBlocks() {
Node node = this.first;
@SuppressWarnings("unchecked")
T[][] result = (T[][]) new Object[this.nNodes][this.arraySize];
for(int i = 0; i < this.nNodes; i )
{
for(int j = 0; j < this.arraySize; j )
if(node.a[j] != null)
result[i][j] = node.a[j];
node = node.next;
}
return result;
}
(我是 Java 新手,所以我的措辭會有點奇怪)我試圖制作一種方法,該方法可以從 T 型展開鏈表中創建一個二維陣列。當我使用 Integer 類而不是 T 型別測驗上述方法時,我收到一個錯誤訊息
執行緒“main”中的例外 java.lang.ClassCastException: class [[Ljava.lang.Object; 不能轉換為類 [[Ljava.lang.Integer; ([[Ljava.lang.Object; 和 [[Ljava.lang.Integer; 在加載器‘bootstrap’的 java.basa 模塊中)
所以是的,我想知道是否有任何方法可以在不更改回傳型別的情況下解決此錯誤。提前致謝 :)
uj5u.com熱心網友回復:
y 出一個 T 型展開的鏈表。
這是不可能的。
泛型是編譯器想象力的虛構。一旦您的 Java 代碼變成類檔案,泛型就會完全消失,或者如果它們沒有(簽名中的泛型),JVM 會將其視為注釋。它完全沒有效果。相反,編譯器使用它來生成錯誤或警告并注入不可見的強制轉換。這個:
List<String> x = new ArrayList<String>();
x.add("Hello");
String y = x.get(0);
最終在類代碼中與編譯無法區分:
List x = new ArrayList();
x.add("Hello");
String y = (String) x.get(0);
如果您很難理解這個想法,請嘗試一下。兩者都寫,編譯,運行javap -c -v查看位元組碼。完全相同的。
之所以x.add(5)不能作為替代品,x.add("Hello")只是因為javac不會讓它發生。如果你 hack javac 允許它,你會得到一個很好的類檔案,它驗證也很好。該x.add(5)連將執行就好了。您會在下一行得到 ClassCastException,這僅僅是因為您將 Integer 的實體轉換為 String。
因此,無法在運行時分辨anew ArrayList<String>();和 a之間的區別new ArrayList<Integer>()。明顯地; 泛型消失;那些都只是new ArrayList(),僅此而已。
相比之下,陣列是“具體化的”:它們不是 javac 的想象。你實際上可以在運行時得到這些東西。new String[0]和之間有區別new Integer[0]:
Object[] arr1 = new String[0];
System.out.println(arr1.getClass().getComponentType()); // prints 'String'
這是不可能寫泛型的相同的代碼:
List<?> list1 = new ArrayList<String>();
System.out.println(--nothing you can write here will print String--);
因此,在您的“使用 T 展開的代碼”中,T 不是您可以轉換為實際運行時型別的東西,這意味著不可能制作 T 陣列。
仍然很難相信這一點?仔細閱讀 的 API java.util.List,特別是toArray它包含的各種方法。
看看無引數的:toArray(). 這里有兩種解釋:
- 這個類的設計者是個徹頭徹尾的白癡,因為那個回傳
Object[],這是愚蠢的,因為顯然應該回傳T[]。 - 或者,也許其他事情正在發生并且他們“知道”實際上不可能回傳
T[]那里。
正如這篇文章的其余部分希望已經暗示的那樣,這是第二個原因。
幸運的是,還有另外 2toArray種方法,并且這兩種方法都可以T[]根據您的需要回傳。它們都基于呼叫者努力為您提供 T 型別的概念。
The first version is toArray(T[] in). The toArray code will use the provided array if it is large enough, but if not, it just makes a new one that is the right size and returns it. In practice, you always call listOfStrings.toArray(new String[0]) (you may think new String[list.size()] would be faster - no, that is slower1. A nice example of why writing more complex code because it seems faster is a bad idea. JVMs are far too complex to predict performance like this).
The trick here is that the code in list's toArray will take that array, grab its class (tossing the created array aside), get the component type from that, and then use that to make a new array.
There is another one, too: toArray(IntFunction<T[]> arrayCreator) (you need to look at the javadoc of Collection to see it; it is inherited).
Here we ask the caller to provide code that makes a new array. You use it like this: listOfStrings.toArray(String[]::new).
Pick your poison, or add both. Either trick will work here:
public T[][] getArrayOfBlocks(T[] dummy) {
Class<?> componentType = dummy.getClass().getComponentType();
@SuppressWarnings("unchecked")
T[][] arr = (T[][]) java.lang.reflect.Array.newInstance(componentType, this.nNodes, this.arraySize);
.. code continues here ..
}
or:
public T[][] getArrayOfBlocks(BiFunction<Integer, Integer, T[][]> arrayMaker) {
T[][] arr = arrayMaker.apply(this.nNodes, this.arraySize);
.. code continues here ..
}
Yes, they are both annoying. There are other options but they have significant downsides - the above 2 options are your best bet. That or forget about arrays. Why do you even want a `T[][]` in the first place? Arrays can't grow or shrink, assuming it's not a primitive array (and this isn't, by definition; generics cannot be primitive) they are not more performant, and their toString/equals/hashCode implementations are surprising (that's programmer-ese for 'basically broken'). Their API is non-existent. Why would you want to offer it?
1) In case you desire explanations for this one: It's because the toArray code is hotspot intrinsiced and knows it doesn't need to wipe out the memory space, whereas with `new String[100]`, those 100 references all need to be nulled out first because java guarantees you can't 'see' uninitialized memory.
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/348575.html
