淺談java泛型的類型轉換問題
- 先引出問題如下:
下面代碼編譯時<1>處不報錯,<2>處報錯,爲何?
public class Test {
public static <T> void printList(List<T> lists) {
@SuppressWarnings("unchecked")
List<Object> mLists = (List<Object>) lists;//<1>此處爲什麼不報錯?
System.out.println(mLists.toString());
}
public static void main(String[] args) {
List<Object> list_1 = new ArrayList<Object>();
List<String> list_2 = new ArrayList<String>();
list_1.add("Test");
list_1.add(23);
list_2.add("zhong");
list_2.add("guo");
list_1 = (List<Object>)list_2; //<2>此處是編譯時檢查,顯然這種類型轉換時不被允許的。
printList(list_1);
printList(list_2);
}
}
程序運行結果如下:
我們知道,形如下面的轉換在java中是不被允許的:
class A{}
class B extends A{}
List<B> listB = new ArrayList<B>();
List<A> listA = (List<A>)listB; //編譯報錯
List<B>
是不能轉換爲List<A>
的,這兩種類型看似向上轉型合理,其實是沒有任何繼承關係,理解這點,也就明白剛開始的<2>處爲什麼編譯報錯了。那我們現在討論一下<1>處爲什麼不報錯?
public static <T> void printList(List<T> lists) {
@SuppressWarnings("unchecked")
List<Object> mLists = (List<Object>) lists;//<1>
System.out.println(mLists.toString());
}
我的理解是,調用printList函數時,所涉及到的轉型(針對泛型)是一種動態的運行時轉型(編譯期不做檢查,也沒法做),而根據java泛型的類型擦除,到時不管List<Object>
中的類型參數是什麼都擦除爲List<Object>
,所以從這個意義上說,只要是List類型,不管類型參數是什麼,理論上都不會報錯。下面我們對其進行驗證如下:
- List 類型參數爲A(
List<A>
)
public class Test {
class A{}
public static <T> void printList(List<T> lists) {
@SuppressWarnings("unchecked")
List<A> mLists = (List<A>) lists; //<1>
System.out.println(mLists.toString());
}
public static void main(String[] args) {
List<Object> list_1 = new ArrayList<Object>();
List<String> list_2 = new ArrayList<String>();
list_1.add("Test");
list_1.add(23);
list_2.add("zhong");
list_2.add("guo");
// list_1 = (List<Object>)list_2; //(1)此處是編譯時檢查,顯然這種類型轉換時不被允許的。
printList(list_1);
printList(list_2);
}
}
編譯後結果如下:
其中類型A是我們自己定義的內部類型,同樣不報錯,同樣的執行結果。
由此可驗證我們的想法。
備註:
剛開始接觸java,理解不深,今天看LoaderManager時,看到如下調用,頓感困惑,所以纔有這一篇文章,下面我將LoaderManager中令我困惑的地方貼出,以供同仁們參考,有理解不正確的地方望大膽指出。
@SuppressWarnings("unchecked")
@SuppressWarnings("unchecked")
public <D> Loader<D> initLoader(int id, Bundle args, LoaderManager.LoaderCallbacks<D> callback) {
if (mCreatingLoader) {
throw new IllegalStateException("Called while creating a loader");
}
LoaderInfo info = mLoaders.get(id);
if (DEBUG) Log.v(TAG, "initLoader in " + this + ": args=" + args);
if (info == null) {
// Loader doesn't already exist; create.
//注意此處callback的類型轉換,
info = createAndInstallLoader(id, args, (LoaderManager.LoaderCallbacks<Object>)callback);
if (DEBUG) Log.v(TAG, " Created new loader " + info);
} else {
if (DEBUG) Log.v(TAG, " Re-using existing loader " + info);
info.mCallbacks = (LoaderManager.LoaderCallbacks<Object>)callback;
}
if (info.mHaveData && mStarted) {
// If the loader has already generated its data, report it now.
info.callOnLoadFinished(info.mLoader, info.mData);
}
return (Loader<D>)info.mLoader;
}