[英]Type erasure Generics
編譯時在 new T[5] 處發生錯誤 => error: generic array creation 根據我的理解,該數組是在編譯時創建的,因為我們在編譯時不知道 T 的類型我們不能實例化一個數組。 但是,如果 T 在編譯時被擦除並更改為 Object 那么為什么仍然會出現此錯誤? 因為我們可以創建一個 Object 的數組。
// Before Compiling
public class GenericClass<T> {
GenericClass(){
T[] obj = new T[5];
}
}
// After Compiling
public class GenericClass {
GenericClass() {
Object[] obj = new Object[5];
}
}
類似的情況,比如,
public class GenericClass<T> {
GenericClass(){
T obj = new T(); }}
/* error :required: class
found: type parameter T
where T is a type-variable:
T extends Object declared in class GenericClass*/
根據我的理解,數組是在編譯時創建的
不,數組是在運行時創建的。
並且因為我們在編譯時不知道 T 的類型,所以我們不能實例化一個數組。
正確的。
但是,如果 T 在編譯時被擦除並更改為 Object 那么為什么仍然會出現此錯誤?
因為“它在編譯時被擦除並更改為對象”被過於簡單化了。
此外,generics 和 arrays 彼此不友好。 問題是,在 generics 部分被擦除的地方,arrays不會那樣工作。 你可以這樣做:
String[] x = new String[10];
tellMeTheTypeOfMyArray(x);
void tellMeTheTypeOfMyArray(Object[] o) {
System.out.println("Your array's component type is: " + o.getClass().getComponentType());
}
此代碼將編譯並正常工作,沒有錯誤,並打印:
Your array's component type is: java.lang.String
與 generics 相比,您不能在其中編寫此類方法。 你不可能這樣做:
List<String> x = new ArrayList<String>();
tellMeTheTypeOfMyList(x);
void tellMeTheTypeOfMyList(List<?> o) {
System.out.println("Your list's component type is: " + ??????);
}
工作。 這里不可能有 java 代碼,沒有什么可以代替 ?????? 打印 String,因為該信息在運行時根本不存在了。
想象一下這段代碼:
// This is written by Wahab today.
class Example<T> {
protected T[] arr;
Example() {
this.arr = new T[10];
}
}
它像你想要的那樣工作。 然后我做:
// Written by me, a year later
class Uhoh extends Example<String> {
Uhoh() {
super();
}
void hmmm() {
System.out.println(this.arr.getComponentType());
}
}
我顯然期望,不,要求 - 這會打印java.lang.String
,但它不可能這樣做。 因為這很奇怪和令人困惑,java 有一個規則:如果你編譯你的代碼並且你沒有看到任何關於 generics 問題的警告(並且沒有@SuppressWarnings
將它們消除),那么這種混淆不太可能發生。
允許您編寫new T[]
並以這種愚蠢的方式編寫new Object[]
被認為太過分了。
與java.util.ArrayList
相同的方式: Do not use generics here 。 Arrays 如果您打算在通用代碼中創建它們,則幾乎不應該有T
類型。 如果你的代碼庫中的任何地方都有一個T[]
,那么這意味着你永遠不應該為它做任何事情——讓你new
代碼的調用者為你做。 如果您確實想自己new
new arrays,請不要使用 T,使用Object[]
作為類型,並在需要時轉換為 T。 這就是 java 內置 ArrayList class 的工作原理。 一些摘錄直接從其來源復制粘貼:
transient Object[] elementData; // non-private to simplify nested class access
public E get(int index) {
Objects.checkIndex(index, size);
return elementData(index);
}
@SuppressWarnings("unchecked")
E elementData(int index) {
return (E) elementData[index];
}
這是一個示例,同樣直接來自 ArrayList 的來源(或者更確切地說, java.util.Collection
定義了它,而 ArrayList 繼承了它),您讓調用者為您提供代碼來生成 arrays:
default <T> T[] toArray(IntFunction<T[]> generator) {
return toArray(generator.apply(0));
}
在這里,調用者提供了一個 function,它將一個int
轉換為一個T[]
- 它采用了執行new String[10]
的概念並將其轉換為一個 function,然后您將其傳遞給 toArray 方法,該方法將使用它(感覺在這里可以隨意忽略它是如何使用它的,這是一個有點奇怪的解決方案。它有效,只是 - 不確定你是否應該學習那部分的課程)。
你像這樣使用它:
List<String> listOfStrings = ...;
String[] convertedToArray = listOfStrings.toArray(String[]::new);
Java arrays 在運行時知道它們的組件類型。 創建數組時,必須在運行時提供組件類型。 但是在您的GenericClass
中,它不能這樣做,因為它在運行時不知道T
是什么。 如果它創建一個Object[]
,那么 object 將具有錯誤的運行時 class,並且如果T
不是Object
,則該實例與類型T[]
不兼容。 你是對的,在 class 中,沒有立即出錯。 但是如果變量是T[]
的聲明暴露給外部 scope,它期望T
是一個更具體的類型,它可能會導致ClassCastException
:
// Before type erasure
class GenericClass<T> {
T[] obj;
GenericClass() {
obj = new T[5]; // if hypothetically you could do this
}
T[] getObj() {
return obj;
}
}
class MyCode {
public static void main(String[] args) {
GenericClass<String> foo = new GenericClass<>();
String[] strings = foo.getObj(); // no casts needed; no warnings
}
}
// After type erasure
class GenericClass {
Object[] obj;
GenericClass() {
obj = new Object[5];
}
Object[] getObj() {
return obj;
}
}
class MyCode {
public static void main(String[] args) {
GenericClass foo = new GenericClass();
String[] strings = (String[]) foo.getObj(); // ClassCastException at runtime
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.