淺談java泛型的類型轉換問題

淺談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;
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章