Generics Types 泛型學習筆記

Generics Types 泛型學習筆記<>

作者:冰雲
時間:2004-02-24
聯繫:icecloud(AT)sina.com
Bloghttp://icecloud.51.net

首先我要特別感謝Schlemiel,他指出了類型協變的問題。其實我以前也看到過gigix翻譯的那篇文,但是沒看懂也沒經大腦。經Schlemiel一提醒,我才意識到,這是泛型的規範。再翻出來看,如醍醐灌頂,真良師益友也。

我寫文章的目的,就是爲了能夠找到更多同好進行交流。沒有交流,沒有氛圍,絕對不會有提高。謝謝Schlemiel,每次讀他的文章或評論,都能受到很大的啓發。

如果需要深入瞭解泛型,請閱讀程序員雜誌20037期,或

http://gigix.cool2u.net/download/Variance_in_Java.pdf

 

ok,繼續上次的學習筆記。這篇22頁的文章正好讀了3天。每天從地鐵東四十條到西直門,每次讀7頁。如果哪位晚上6:30-7:00間看到地鐵上有個人拿着本電腦書在讀,不妨上來問問是不是俺 ^___^||

另:後面文中每章出現了不少重點,我就按照和文中章節一樣的標題。Notes則自己編號。

這次特地把字體變爲14px,不知道各位看起來如何啊?會不會太大?

5 泛型方法(函數)

 

根據上一條,既然通配符類型是隻讀的,那麼怎樣才能在函數中寫入呢?這就引入了泛型函數(Generic Mehtods):

 

 

    <T> static void fromArrayToCollection(T[] a, Collection<T> c) {

       for (T o : a){

           c.add(o); //correct

       }

    }  

    Collection<Object> co = new ArrayList<Object>;

    Collection<String> cs = new ArrayList<String>;

    fromArrayToCollection(new Integer[100], co); //correct

    fromArrayToCollection(new Number[100], cs); // error

 

 

    可以知道,不需要傳遞世紀參數到泛型方法,編譯器會自行推斷類型。

那麼,什麼時候使用泛型方法,什麼時候使用通配符類型呢?可以這樣理解:

 

Note 5: 可以採用泛型方法來保證讀寫操作的類型安全。泛型方法相當於原來的多態方法,它的效果是允許執行時選用不同的參數類型,而通配符方法相當於接受Object的方法。

文:This tells us that the type argument is being used for polymorphism; its only effect is to allow a variety of actual argument types to be used at different invocation sites.

考慮下面的方法,

      

       public static <T, S extends T> void copy (

              List <T> dest, List <S> src) ();

       public static <T> void copy (

              List <T> dest, List <? extends T> src) ();

 

 

    哪一個更清楚明瞭一點?因爲S只用了一次,因此可以用通配符類型取代S。這樣更簡練。通配符類型的另一個好處是,他們可以用在方法外面,作爲值域(fields)、本地變量(local variables)或數組的類型。如

 

      

       static List<List<? extends Shape>> history

              = new ArrayList<List<? extends Shape>>();

 

       public void drawAll(List<? extends Shape> shapes){

           history.addLast(shapes);

       }

 

 

說實話,我看到上面的變量定義簡直想罵人。這個是定義了一個ListList,也就是history是保存List<? extends Shape>List

 

6 泛型與舊代碼

在泛型代碼中使用舊代碼可能導致類型安全隱患。

 

      

    public interface Inventory {

       void setCats(Collection c);

       Collection getCats();

    }

    Collection<Cat> c = new ArrayList<Cat>();

    c.add(new BlackCat());

    Inventory.setCats(c);

    Collection<Cat> k = Inventory.getCats();

 

 

Note 6: 一般編譯器無法知道Collection引用了何種類型,這樣的沒有帶有類型的Collection稱爲row type。這樣的類型表示一些未知類型,相當於Collection<?>

原文:The compiler has no way of knowing what kind of collection the type Collection refers to. It’s called a raw type. It’s more accurate to say that the type Collection denotes a collection of some unknown type like Collection<?>.

 

這種情況下,把Collection<?>賦值給Collection<Cat>是合法的,但是會出現一個unchecked warning,未檢查警告。表示編譯器無法保證它的正確性。因此使用舊代碼時候請注意unchecked warning.

 

Note 7: 泛型編譯時會被編譯器進行一次稱爲erasure的過程。可以認爲就是將泛型的代碼轉換爲非泛型版本。最終的結果是:JVM不會檢查類型安全和完整性,就算出現了unchecked warning也一樣。Erasure會刪除所有的泛型信息,如將List<String>轉換爲List,所有的類型變量被替換爲最上限的,如Object。並且不管結果代碼如何,都會增加類型轉換。

原文:Generics are implemented by the Java compilers as a front-end conversion called ensure. It just like a source-to-source translation. As a result, the type safety and integrity of the JVM are never at risk, even in presence of unchecked warnings. Erasure throws out  all type information between angle brackets. For example, List<String> is converted into List. All remaining uses of type variables are replaced by the upper bound type variable (usually Object). And, whenever the resulting code isn’t type-correct, a cast to the appropriate type is inserted.

 

       在舊代碼中使用泛型代碼。與上面情況類似,會出現unchecked warning.

 

7 Fine Print 美好藍圖?(搞不懂作者的意圖)

   

       泛型類被所有的它的子類共享。

 

             

    List<String> l1 = new ArrayList<String>();

    List<Integer> l2 = new ArrayList<Integer>();

    assert(l1.getClass()==l2.getClass()); // return true

 

 

Note 8: 所有的泛型類擁有相同的runtime類(其實從上面一條可以知道),並且,靜態變量和方法也會在類的實例間共享。這就是爲何在靜態方法、變量或初始化中引用類型參數是非法的。

原文:All instances of generics class have the same run-time class. As consequence, the static variables and methods of a class are also shared among all the instances. That’s why it is illegal to refer to the type parameters of a type declaration in a static method or initializer, or in the declaration or initializer of a static variable.

 

    另一個隱含的事實,是無法判斷一個類是否instanceof 其泛型類。或類型轉換一個對象到一個泛型類型。

 

             

    Collection<String> cs = new ArrayList<String>;

    if (cs instanceof Collection<String>) {} //非法

    (Collection<String>) cs; // unchecked warning

   

    <T>T badCast(T t, Object o) {return (T)o};// unchecked warning

 

 

Note 9: 類型變量在運行時不存在!這意味着泛型不會佔用時間或空間的性能,但不幸的是,你不能可靠的使用類型轉換。

原文:type variables don’t exist at run time. This means that they entail no performance overhead in either time nor space. It also means that you can’t reliably use them in casts.

 

數組可能無法成爲一個參數類型。除非是一個通配符類型。

 

             

    List<String> [] lsa = new List<String>[10]; // 不允許

                     = new List<?>[10]; // 允許

    Object o = isa;

    Object[] oa = (Object[])o;

    oa[1] = new ArrayList<Integer>();

    String s = lsa[1].get(); //允許的情況下,runtime error

 

 

可以聲明類型變量的數組,但是不能初始化(new)建立一個新數組。如new T[100],因爲類型變量運行時並不存在。

 

 

詞彙表:

參數類型:parameterized type,形如:new ArrayList<?>new ArrayList<String>

類型變量:type variable,形如:<T> T getCollection()…

通配符類型:wildcard type,形如:new ArrayList<?>

有界通配符類型:bounded wildcard type,形如:new ArrayList<? extends Object>

原生類型:raw type,指在泛型代碼中使用Collection, ArrayList等舊形式

未檢查警告:unchecked warningJava1.5代碼安全警告。

 

To be continue ...

      

 

版權聲明:

本文由冰雲完成,首發於CSDN,作者保留中文版權。

未經許可,不得使用於任何商業用途。

歡迎轉載,但請保持文章及版權聲明完整。

如需聯絡請發郵件:icecloud(AT)sina.com

 



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章