JWorld@TW the best professional Java site in Taiwan
      註冊 | 登入 | 全文檢索 | 排行榜  

» JWorld@TW » Java SE 討論區  

按列印兼容模式列印這個話題 列印話題    把這個話題寄給朋友 寄給朋友   
reply to postflat modego to previous topicgo to next topic
本主題所含的標籤
無標籤
作者 Generics 簡介 [精華]
jocosn





發文: 102
積分: 5
於 2005-05-05 19:23 user profilesend a private message to userreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
最近看 generics 作的筆記。非本人獨創,資料來源為網路蒐集。因為整理耗費且間隔多日,所以應該會有錯誤,若是發現錯誤請留言指正。
至於為什麼貼在這區,不貼在新手區?因為這區的 2 位版主很厲害,可能會提供更多進一步的解說。就醬。

紅色斜體字在程式碼實作時須自行替換。橘色 [] 表示非必要可省略。
請注意,如果你直接拷貝程式碼準備跑跑看,你必須將全型空白(排版用)轉為半形空白。請使用 IDE 工具提供的替代功能。
*********************************************************

泛型(Generics)  

1. Generics 可提供 compile-time type 檢查。
2. 泛型的使用:
 ◇ 類別 (註:介面泛型類似類別泛型,此處不作介紹)
 •定義泛型 Shy
  A> [class-modifiers] class GenericsName<T1 [, T2, T3, ...]> { ...... }
   泛型由泛型名稱(generic type name)、角括號 "<>"、包含在角括號內的型態參數(type parameter)組成。
   在角括號 "<>" 內的型態參數( Tx ) 須為某種參考型態。

   [範例]
1
2
3
4
5
6
7
8
9
10
11
12
   class MyHashTable<Key, Value> {  // MyHashtable 稱為 generic type name,Key 及 Value 為 type parameter
     ......
     Value put(Key k, Value v) {...} 
     Value get(Key k) {...} 
   }
   class Demo { 
     public static void main(String[] args) { 
       MyHashTable<Integer, String> h = new MyHashTable<Integer, String>( ); 
       h.put(new Integer(0), "value"); 
       ......
     }
   }  

  B> 泛型類別(/介面)可被繼承(/實作),子類別(實作介面)無論是否宣告父類別(介面)的型態參數皆會繼承型態參數,並且可以宣告新的型態參數。
   子類別若沒有宣告父類別(/介面)的全部型態參數,則繼承的型態參數會自動變為 Object 型別。 (註:參考偶像良葛格的筆記) (以上這段請見下面 Duncan版主的更正)
    [範例]
1
2
3
4
5
6
    class MyGenericsSuper<T1, T2> {
      ......
    }
    class MyGenericsSub<T1, T2, T3> extends MyGenericsSuper<T1, T2> {
      ......
    }


    [範例]
1
2
    interface MyGenericsI<T1, T2> { /* ...... */  }
    class MyGenericC<T1, T2> implements MyGenericsI<T1, T2> { /* ...... */ }


 •宣告/建立泛型物件(Instantiating a Generic Type) Shy
  A> 宣告泛型物件
   1> concrete parameterized type
    須使用泛型名稱(generic type name),後面加上角括號"<>",角括號"<>" 內須以 class 或 interface 名稱取代泛型宣告的型態參數(type parameter)來指定泛型真正的使用型態。

    [範例] concrete parameterized type
1
2
3
4
5
    Vector<String>       // Vector<String> 稱為 parameterized types
    Seq<Seq<A>>
    Seq<String>.Zipper<Integer>
    Collection<Integer>
    Pair<String, String>


    [範例]
1
    LinkedList<LinkedList<String>> texts 


   2> wildcard type & bounded wildcard
    a> wildcard type (通配型)
     角括號內若使用問號 "?" 而不是 class 或 interface 名稱,表示宣告泛型型別為通用型別,可使用任何的參考型態。
      "?" 表示未知型態(unknown type)。
    b> bounded wildcard (有限制的通配型)
     wildcard type 可限制型別的範圍(bounds)。
     使用 extends 關鍵字可指定欲使用的型態必須是某個型別(介面或類別)或其衍生型別。
     使用 super 關鍵字指定使用某個型別(介面或類別)及其父型別。
     使用 "&" 符號可作多重限制(Multiple bounds)。

     [範例]
1
     MyGenericsClass<? super Integer> obj;  // 只接受 Integer 及其父類別型態的物件


     [範例] can create a variable of type Matrix<Integer> or Matrix<Float>,
1
     public class Matrix<V extends Number> { ...... }


     [範例]
1
     class C<T extends Comparable<? super T> & Serializable>


     [範例]
1
2
3
4
5
6
7
8
     interface ConvertibleTo<A> {
       A convert();
     }
     class ReprChange<A implements ConvertibleTo<B>, B implements ConvertibleTo<A>> {   // error, implements 須改為 extends
       A a;
       void set(B x) { a = x.convert(); }
       B get() { return a.convert(); }
     }

   3> raw type
    宣告泛型物件只使用泛型名稱(generic type name),則預設會使用 Object 型別,使用此類物件須自行轉換型態,且編譯器會發出 warning 訊息。
    [註] 原生類型:raw type,指未使用泛型,如 Collection、ArrayList 等 Java 5 之前的舊形式,或指僅使用泛型名稱(generic type without type arguments)。
      Java Language Specification 第 3 版指出以後的版本可能會禁止使用 raw type。

    [範例]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
    class MyGenerics<T> {
      T value;
      void setter(T value) {
        this.value = value;
      }
      T getter( ) {
        return value;
      }
    }
 
    class MyGenericsDemo {
      public static void main(String[] args) {
        MyGenerics<Integer> myObj1 = new MyGenerics<Integer>( );  // 使用  parameterized type
        myObj1.setter(new Integer(10));
        System.out.println(myObj1.getter( )) ;    // 10
 
        MyGenerics myObj2 = new MyGenerics( );  // 使用 raw type
        myObj2.setter((Object)new Integer(10));  // warning: [unchecked] unchecked call to setter(T) as a member of the raw type MyGenerics
        Integer i = (Integer) myObj2.getter( );
        // System.out.println(i == myObj1);  // Error: incomparable types: java.lang.Integer and MyGenerics
        // System.out.println(i == myObj2);  // Error: incomparable types: java.lang.Integer and MyGenerics<java.lang.Integer>
        System.out.println(myObj2.getClass( ) == myObj1.getClass( ));  // true
      }
    }

  B> 建立泛型物件須使用 new 關鍵字,並指定型態參數的值(values of the type parameters)。此種指定值的型態參數稱為 parameterized type。

   [範例]
1
   MyGenericClass<? extends java.lang.Number> obj = new MyGenericClass<Integer>( );  // Bounded wildcard


   [範例]
1
2
3
4
   LinkedList<String> myStrings;    // A variable of type LinkedList<String>
   myStrings = new LinkedList<String>( );
   或
   LinkedList<String> myStrings = new LinkedList<String>( );  //define an object when you define the variable


 ◇ methods 使用泛型
  [MethodModifiers] [TypeParameters] ResultType MethodName(...) [throws ....] ;{ ... }

 不論 class(interface) 是否宣告泛型,methods 皆可使用泛型。

 [範例]
1
2
3
4
5
6
7
8
 // T -- return type, <T>-- 指明 agrument 的傳入型態
 public <T>  T  ifThenElse(boolean b, T first, T second) {
   return b ? first : second;
 }
 ......
 String s = ifThenElse(true, "a", "b");   // a
 Integer i = ifThenElse(false, new Integer(1), new Integer(2));  //2
 //String s = ifThenElse(b, "PI", new Float(3.14));  // Error


 [範例]
1
2
3
 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) { ...... } ;


 [範例]
1
2
3
4
5
6
7
8
9
10
11
12
 public class M {
    public static <T extends Comparable> T minimum(T a, T b) {
       if(a.compareTo(  b) <= 0) return a;
         else return b;
    }
   public static void main(String[] args) {
     Integer b1  = new Integer(2);
     Integer b2  = new Integer(5);
     Integer min = minimum(b1, b2);
     System.out.println("Minimum of (2,5) : " + min);
   }
 }


 [範例]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
 public <T extends Animal> void printAnimals(Collection <T> animals) {  // Version 1
   for (Animal nextAnimal: animals) {
     System.out.println(nextAnimal);
   }
 }
---------------------------------------------------------------------------------------------
 Version 1 的 method 內並未用到 T ,可改成 "wildcard" 型式
 public void printAnimals(Collection <? extends Animal> animals) {   // Version 2
   for (Animal nextAnimal: animals) {
     System.out.println(nextAnimal);
   }
 }
---------------------------------------------------------------------------------------------
 method 內使用 Type Parameter
 public <T extends Animal> void printAnimals(Collection<T> animals) {  // Version 3
   for (T nextAnimal: animals) {
     System.out.println(nextAnimal);
   }
 }
---------------------------------------------------------------------------------------------
 以 ? 取代 T 會造成錯誤
 public void printAnimals(Collection<? extends Animal> animals) {  // Version 4
   for (? nextAnimal: animals) {  // illegal start of expression
     System.out.println(nextAnimal);
   }
 }


3. 泛型使用的注意事項 DeadDead
 A> static members 內不能使用 type parameters
  [範例]
1
2
3
4
5
6
7
8
  class C<T> {
      static void m( ) {
          T t;  // Error: non-static class T cannot be referenced from a static context
      }
      static class D {
          C<T> t;  // Error: non-static class T cannot be referenced from a static context
      }
  }

 B> primitive types 不能使用在泛型
 C> "Naked" type parameters (指不包括角括號的型態參數,如 C<T>,指的是 T ) 不能做的事:
  不能用在鑄型(casts)、instanceof、或 new 運算上。
  不能使用在定義 class 時的 implements 或 extends 子句內
  不能用來建立物件,也不能用作父類型 (class Foo<T> extends T),不能用於類別實字(class literal)。

  [範例]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
  import java.util.Hashtable;
 
  interface Registry {
    public void register(Object o);
  }
 
  class C<T> implements Registry {
    int counter = 0;
    Hashtable<Integer, T> values;
  
    public C( ) {
      values = new Hashtable<Integer, T>( );
    }
  
    public void register(Object obj) {
      values.put(new Integer(counter), (T)obj);  // warning: [unchecked] unchecked cast
      counter++;
    }
  }


  [範例]
1
2
3
  Collection<String> cs = new ArrayList<String>;
  if ( cs instanceof Collection<String> )   {  ....  }   //illegal
  <T> T badCast(T t, Object o) { return (T)o };  // unchecked warning


 D> 異常型態(exception type)、列舉(enumeration)或匿名內部類(anonymous inner class)不能使用型態參數(type paramemter)。
  type variables 雖不允許使用在 catch 子句內,但可用在 methods 利用 throws 所丟出的例外清單中。

  如果 generic class 為 Throwable 的 subclass(直間或間接),會產生 compile-time error。因為 throw/catch mechanism 只能作用在 non-generic classes 上。

  [範例]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
  import java.io.*;
  interface MyExecutor<E extends Exception> {
    void execute() throws E;
  }
  public class GenericExceptionTest {
    public static void main(String args[]) {
      try {
        MyExecutor<IOException> e = new MyExecutor<IOException>() {
          public void execute() throws IOException {
            // code here that may throw an IOException or a subtype of  IOException
          }
        };
        e.execute();
      } catch(IOException ioe) {
        System.out.println("IOException: " + ioe);
        ioe.printStackTrace();
      }
    }
  }


 E> 泛型不是 covariant
  如果 MySub 是 MySuper 的子類型,當 MyG 作泛型類別宣告時,MyG<MySub>不是 G<MySuper>的子型態(subtype)。
  例 List<Object> 不是 List<String> 的父型態。

  [範例]
1
2
3
4
5
  Integer[] intArray = new Integer[10];  
  Number[] numberArray = intArray;  // OK
  List<Integer> intList = new ArrayList<Integer>();
  List<Number> numberList = intList;  // error
  numberList.add(new Float(3.1415));  // error


 F> 通配泛型(type wildcards) 雖可檢索元素(retrieve elements)與移除元素,但不能增加元素。

  [範例]
1
2
3
4
5
6
  List<Integer> li = new ArrayList<Integer>( );
  li.add(new Integer(42));
  List<?> lu = li;  // OK
  System.out.println(lu.get(0));
  //lu.add(new Integer(43));  // error
  lu.clear( );  // OK, don't depend on the compiler having to know anything about the type parameter of lu


 G> 泛型在進行編譯時會進行 erasure 的過程,會刪除所有的泛型資訊。

  [範例] 以下的範例會產生錯誤:duplicate class
1
2
3
4
5
6
7
  class ShoppingCart<T extends DVD>{
    ……
  }
 
  class ShoppingCart<T extends VideoTape>{
    ……
  }


  [範例] 以下的範例會產生錯誤:name crash, have the same erasure
1
2
3
4
5
6
7
8
  class TwoForOneSpecial<T extends Rentable, W extends Rentable> {
    public void add(T newRentable) {
      ……
    }
    public void add(W newRentable) {
      ……
    }
  }


  [範例]
1
2
3
4
5
6
7
  public class GenericClass<E> {
      ……
      public Class<E> getElementType() {
          return E.class;  // error
      }
      ……
  }


  [範例]
1
2
3
  Vector<String> x = new Vector<String>();
  Vector<Integer> y = new Vector<Integer>();
  return x.getClass() == y.getClass();  // true


  [範例]
1
2
3
  LinkedList<Float> floatList = new LinkedList<Float>( );
  List<Float> moreFloats = floatList;  // legal
  LinkedList<Number> numberList = floatList;  // error


  [範例] parameterized type 可 cast 為 raw type
1
2
3
4
  List<Integer> ints = new LinkedList<Integer>( );
  List oldList = ints;  // We can widen (due to backwards compatibility)
  oldList.add("Hello");  // This line should be illegal, but it happily compiles and runs
  Integer i = ints.get(0);  // compile warning, runtime error -- ClassCastException at runtime


  [範例] Breaking type safety with reflection
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
  import java.util.ArrayList;
  import java.util.List;
 
  public class BadIdea {
    private static List<Integer> ints = new ArrayList<Integer>( );
    public static void fillList(List<Integer> list) {
      for (Integer i : list) {
        ints.add( i );
      } // for
    } // fillList(List<Integer> list)
 
    public static void printList( ) {
      for (Integer i : ints) {
        System.out.println( i );
      } // for
    } // printList( )
 
    public static void main(String[] args) {
      List<Integer> myInts = new ArrayList<Integer>( );
      myInts.add(1);
      myInts.add(2);
      myInts.add(3);
      System.out.println("Filling list and printing in normal way...");
      fillList(myInts);
      printList( );
 
      try {
        List list = (List)BadIdea.class.getDeclaredField("ints").get(null);
        list.add("Illegal Value!");
      } catch (Exception e) {
        e.printStackTrace( );
      } // try-catch
    
      System.out.println("Printing with illegal values in list...");
      printList( );
    } //main()
  } 


 H> 陣列無法使用泛型,除非是一個通配符類型(unbounded wildcard)。
   可以宣告型態變數(concrete parameterized type)的陣列,但是不能使用 new 建立陣列。如 new T[100],因為型態變數會被 erasure。因此你只能指派此種陣列為 null。
或是使用反射機制和類別實字(ClassName.class)解決。

  [範例]
1
2
3
4
5
6
  List<String> [] lsa = new List<String>[10];  // 錯誤
          = new List<?>[10];   // OK
  Object o = isa;
  Object[] oa = (Object[])o;
  oa[1] = new ArrayList<Integer>( );
  String s = lsa[1].get( );  // error


***********************************************
看完上面的文章之後,想測驗自己對 Generics 的了解,可以試試看下面的小測驗 Thumbs up
http://www.grayman.de/quiz/java-generics-en.quiz

Thumbs up 如果你有任何疑問,你幾乎可以在下列這個網站找到你要的解答 Thumbs up
http://www.langer.camelot.de/GenericsFAQ/JavaGenericsFAQ.html#Fundamentals%20of%20Java%20Generics

***********************************************
參考
Generics FAQs
了解泛型
Generic Types, Part 1
Erasure and Accidental Compile-Time Conflicts
Not wild enough
Wildcards in the Generics Specification
http://www.devx.com/Java/Article/16024
http://java.sun.com/j2se/1.5/pdf/generics-tutorial.pdf --Sun 的教學
http://www.davidflanagan.com/blog/000027.html --Generics Glossary

無痛苦 generics
http://www-106.ibm.com/developerworks/java/library/j-djc02113.html
http://www-106.ibm.com/developerworks/java/library/j-djc03113.html
http://www-106.ibm.com/developerworks/java/library/j-djc04093.html
http://www-106.ibm.com/developerworks/java/library/j-djc05133.html


jocosn edited on 2006-03-11 05:14
reply to postreply to post
話題樹型展開
人氣 標題 作者 字數 發文時間
56684 [精華] Generics 簡介 jocosn 15402 2005-05-05 19:23
28914 Re:Generics 簡介 jocosn 53 2005-05-07 02:38
13710 Re:Generics 簡介 koji 133 2008-08-27 09:59
29911 Re:Generics 簡介 jocosn 820 2005-05-07 16:43
26000 Re:Generics 簡介 jocosn 319 2006-01-04 03:46
11491 Re:Generics 簡介 leonboy 98 2009-06-08 16:43
25465 Re:Generics 簡介 Mabel 118 2006-02-09 09:42
25046 Re:Generics 簡介 jocosn 489 2006-03-05 19:23
13175 Re:Generics 簡介 ycdta 398 2008-11-18 09:32
25110 Re:Generics 簡介 Duncan 834 2006-03-09 12:30
22873 Re:Generics 簡介 jiangshachina 116 2006-09-18 15:49
22748 Re:Generics 簡介 jiangshachina 48 2006-09-22 14:27
23472 Re:Generics 簡介 riceandabc 4 2006-12-20 16:16
15356 Re:Generics 簡介 serialeasy 10 2008-03-06 09:21
» JWorld@TW »  Java SE 討論區

reply to postflat modego to previous topicgo to next topic
  已讀文章
  新的文章
  被刪除的文章
Jump to the top of page

JWorld@TW 本站商標資訊

Powered by Powerful JuteForum® Version Jute 1.5.8