我有一個名為Foo<T>.
public class Foo<T> {
public void doSomething() {
Class<T> klass = T.class; // This won't compile
}
}
經過一番谷歌搜索,我發現由于java通過執行型別擦除來實作泛型,因此它不知道運行時型別T并拒絕編譯。但是,如果我嘗試這個......
public class Foo<T> {
public void doSomething(T... ts) {
Class<T> klass = (Class<T>) ts.getClass().getComponentType();
System.out.println(klass);
}
}
...并運行以下代碼...
Foo<String> foo1 = new Foo<>();
foo1.doSomething("foo", "bar"); // class java.lang.String
foo1.doSomething(); // class java.lang.String
Foo foo2 = foo1;
foo2.doSomething("foo", "bar"); // class java.lang.Object
foo2.doSomething(); // class java.lang.Object
Foo<? super String> foo3 = foo1;
foo3.doSomething("foo", "bar"); // class java.lang.Object
foo3.doSomething(); // class java.lang.Object
第一個列印class java.lang.String而其他兩個列印class java.lang.Object,這對我來說沒有多大意義 - 物件的行為如何取決于它分配給的變數型別?另外,如果java做了型別擦除,它又怎么知道型別引數實際上java.lang.String是第一種情況呢?而且,由于我無法分配Objectto T,java 如何分配Object[]to T[]?似乎它破壞了型別安全!
我想 java 在這里隱式地傳遞了運行時類,因為它不能從引數中推斷出來,因為在第二個方法呼叫中根本沒有引數。我想知道 java 在幕后做了什么,如果有人能告訴我更多關于通用可變引數的資訊,我會很高興。
我是 StackOverflow 的新手。如果我違反了一些規則,請告訴我:)
uj5u.com熱心網友回復:
首先,可變引數只是陣列引數的語法糖。所以:
clazz.varArgsMethod("A", "B");
與(并且可以作為)相同:
clazz.varArgsMethod(new SomeElementType[]{"A", "B"});
whereSomeElementType由編譯器決定。
在呼叫站點:
Foo<String> foo1 = new Foo<>();
foo1.doSomething("foo", "bar");
foo1.doSomething();
編譯器知道這doSomething是一個可變引數方法,并且需要 aT[]作為可變引數引數。因為它知道那T是String為了 a Foo<String>,它知道那T必須是String。所以它將這段代碼編譯為:
Foo<String> foo1 = new Foo<>();
foo1.doSomething(new String[]{"foo", "bar"});
foo1.doSomething(new String[]{});
的String[]陣列型具有String組件型別。該方法傳遞了 的實體String[],可以反射性地查詢它們的組件型別。
其他兩種情況基本相同:T由 的型別確定Foo:
- 對于 raw
Foo,T被擦除。的擦除T是Object - 對于
Foo<? super String>, 的上界T是Object
因此,它創建并傳遞一個Object[].
uj5u.com熱心網友回復:
...而且,由于我無法將 Object 分配給 T,java 如何將 Object[] 分配給 T[]?似乎它破壞了型別安全!
是的,你是對的。泛型引數型別的可變引數實際上確實違反了 Java 自己的型別安全規則。
為什么?
因為陣列是協變的(和具體化的)而泛型是不變的(被擦除的,或非具體化的)。用 Joshua Bloch 的話來說,“陣列和泛型不能很好地結合在一起。” Java 語言的開發人員做出了一個非常深思熟慮的決定來實作 varargs 語言功能,盡管它違反了型別安全。他們得出結論,擁有可變引數(被反射和注釋工具廣泛使用)的價值證明了它們的風險和局限性。
在 Java 中,您不能編譯此代碼:
T[] genericArray = new T[SIZE];
這是非法的,因為根據 Java 自己的型別安全規則,實體化非具體型別的陣列不是型別安全的。然而,當客戶端呼叫具有泛型型別的 varargs 引數的方法時,運行時系統將實體化該泛型型別的陣列!它不是型別安全的!
如果不小心使用 varargs 系統,ClassCastException程式中可能會出現 s 或其他錯誤。本質上,開發人員和運行時系統之間隱含了一個不可執行的合同:
由通用 varargs 引數產生的陣列...
- 只會被讀取
- 永遠不會被寫入
- 結果永遠不會回傳
- 不會傳遞給另一個客戶端(除非該客戶端也承諾只從陣列中讀取)
換句話說,開發人員承諾僅將生成的通用陣列用作完成計算的可變數量的引數。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/323555.html
