我有一個TBase我知道的串列TChild,其中TChild : TBase. 如果我只有KClass<TChild>,是否可以投射List<TBase>到List<TChild>?
fun <TChild : BaseType> castList(list: List<BaseType>, clazz: KClass<TChild>): List<TChild> {
// is this possible?
}
我知道如果List沒有參與,我可以將一個實體轉換TBase為TChild:
clazz.cast(item)
我正在嘗試做的可能嗎?希望避免具體化的解決方案。
uj5u.com熱心網友回復:
是的。型別斷言。
請記住,泛型是編譯器的想象。JVM 不知道泛型是什么,也不關心。大多數泛型被洗掉(被洗掉并且在變成類檔案的程序中無法生存)。剩下的少數部分(簽名中的泛型)被 JVM 視為注釋。java.exe完全忽略它們。
它們僅用于使 kotlinc/javac 生成錯誤、警告和語法糖。就是這樣。
您可以進行型別斷言。請注意,這些是未經檢查的。您告訴編譯器,您將保證有問題的串列 [A] 當前僅包含TChild[B] 的實體,并且將來在此代碼運行的整個持續時間內,沒有任何內容會改變這一點(沒有代碼最終會將非 TChild 參考添加到此串列中。
如果串列是不可變的,那么 [B] 部分顯然根本不相關。
編譯器會相信你的話。如果事實證明你錯了,你會得到ClassCastException包含零強制轉換的行。因為這有點奇怪,javac會就此警告您,但您可以取消此警告。
在java中看起來像這樣:
List<Object> list1 = new ArrayList<Object>();
List<String> list2 = (List<String>) list1; // warning here
list1.add(5.0); // works.
list2 = (List<String>) list1; // still works. (and warns).
String a = list2.get(0); // look ma, ClassCastException without a cast!
抑制它,@SuppressWarnings("unchecked")在本地 var decl 或方法上。
在 kotlin 中它看起來很相似:list as List<TChild>投射它,@Suppress("UNCHECKED_CAST")抑制警告。
另一種方法是實際迭代串列并確保串列實際上包含正確的型別,當場拋出 ClassCastException,而不是向堆中注入一個定時炸彈,該定時炸彈在稍后某些代碼觸及串列時觸發:
List<Object> obj = new ArrayList<Object>();
obj.add(5.0);
List<String> strings = new ArrayList<String>();
for (Object o : obj) strings.add((String) o);
當然,這需要O(N)時間;任何檢查串列的嘗試都將是O(N),因為泛型被洗掉,因此除了遍歷批次之外,在運行時無法確定。它在 kotlin 中非常相似。
uj5u.com熱心網友回復:
如果你真的確定它只包含TChild物件并且你不會在轉換后向它添加任何其他物件(List只讀,不是不可變的),那么你可以像這樣進行未經檢查的轉換:
fun <TChild : BaseType> castList(list: List<BaseType>, clazz: KClass<TChild>): List<TChild> {
@Suppress("UNCHECKED_CAST")
return list as List<TChild>
}
如果您更喜歡在投射時實際檢查專案型別,請執行以下操作:
fun <TChild : BaseType> castList(list: List<BaseType>, clazz: KClass<TChild>): List<TChild> {
return list.map { clazz.cast(it) }
}
請注意,第二個解決方案會創建串列的副本,因此這不是完全強制轉換。如果您打算在轉換后修改原始串列,這一點很重要,因為正如我所說,List它不是不可變的,而是只讀的。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/311158.html
