Java學習_泛型

  • 什麼是泛型。
      • Java標準庫提供的ArrayList內部就是一個Object[]數組,配合存儲一個當前分配的長度,就可以充當「可變數組」。
        public class ArrayList {
            private Object[] array;
            private int size;
            public void add(Object e) {...}
            public void remove(int index) {...}
            public Object get(int index) {...}
        }
      • 如果用上述ArrayList存儲String類型,會有這麼幾個缺點:

        • 需要強制轉型;

        • 不方便,易出錯。

        • 程式碼必須這麼寫:
          ArrayList list = new ArrayList();
          list.add("Hello");
          // 獲取到Object,必須強制轉型為String:
          String first = (String) list.get(0);

          很容易出現ClassCastException,因為容易「誤轉型」。

          list.add(new Integer(123));
          // ERROR: ClassCastException:
          String second = (String) list.get(1);

          要解決上述問題,我們可以為String單獨編寫一種ArrayList。

          public class StringArrayList {
              private String[] array;
              private int size;
              public void add(String e) {...}
              public void remove(int index) {...}
              public String get(int index) {...}
          }

          這樣一來,存入的必須是String,取出的也一定是String,不需要強制轉型,因為編譯器會強制檢查放入的類型。

          StringArrayList list = new StringArrayList();
          list.add("Hello");
          String first = list.get(0);
          // 編譯錯誤: 不允許放入非String類型:
          list.add(new Integer(123));

          問題暫時解決。然而,新的問題是,如果要存儲Integer,還需要為Integer單獨編寫一種ArrayList。實際上,還需要為其他所有class單獨編寫一種ArrayList。

          這是不可能的,JDK的class就有上千個,而且它還不知道其他人編寫的class。

          為了解決新的問題,我們必須把ArrayList變成一種模板:ArrayList<T>

          public class ArrayList<T> {
              private T[] array;
              private int size;
              public void add(T e) {...}
              public void remove(int index) {...}
              public T get(int index) {...}
          }

          T可以是任何class。這樣一來,我們就實現了:編寫一次模版,可以創建任意類型的ArrayList。

          // 創建可以存儲String的ArrayList:
          ArrayList<String> strList = new ArrayList<String>();
          // 創建可以存儲Float的ArrayList:
          ArrayList<Float> floatList = new ArrayList<Float>();
          // 創建可以存儲Person的ArrayList:
          ArrayList<Person> personList = new ArrayList<Person>();

          這樣一來,既實現了編寫一次,萬能匹配,又通過編譯器保證了類型安全:這就是泛型。

    向上轉型

      • 在Java標準庫中的ArrayList<T>實現了List<T>介面,它可以向上轉型為List<T>。
        public class ArrayList<T> implements List<T> {
            ...
        }
        
        List<String> list = new ArrayList<String>();

        類型ArrayList<T>可以向上轉型為List<T>。不能把ArrayList<Integer>向上轉型為ArrayList<Number>List<Number>。ArrayList<Integer>和ArrayList<Number>兩者完全沒有繼承關係。

  • 使用泛型

    • 使用ArrayList時,如果不定義泛型類型時,泛型類型實際上就是Object

    • 編譯器如果能自動推斷出泛型類型,就可以省略後面的泛型類型。

      // 可以省略後面的Number,編譯器可以自動推斷泛型類型:
      List<Number> list = new ArrayList<>();
    • 泛型介面

      • 除了ArrayList<T>使用了泛型,還可以在介面中使用泛型。例如,Arrays.sort(Object[])可以對任意數組進行排序,但待排序的元素必須實現Comparable<T>這個泛型介面。

        public interface Comparable<T> {
            /**
             * 返回負數: 當前實例比參數o小
             * 返回0: 當前實例與參數o相等
             * 返回正數: 當前實例比參數o大
             */
            int compareTo(T o);
        }

        可以直接對String數組進行排序。

        // sort
        import java.util.Arrays;
        
        public class Main {
            public static void main(String[] args) {
                    String[] ss = new String[] { "Orange", "Apple", "Pear" };
                    Arrays.sort(ss);
                    System.out.println(Arrays.toString(ss));    
                    }
        }

        這是因為String本身已經實現了Comparable<String>介面。如果換成我們自定義的Person類型試試。

         1 // sort
         2 import java.util.Arrays;
         3 
         4 public class Main {
         5     public static void main(String[] args) {
         6         Person[] ps = new Person[] {
         7             new Person("Bob", 61),
         8             new Person("Alice", 88),
         9             new Person("Lily", 75),
        10         };
        11         Arrays.sort(ps);
        12         System.out.println(Arrays.toString(ps));
        13 
        14     }
        15 }
        16 
        17 class Person {
        18     String name;
        19     int score;
        20     Person(String name, int score) {
        21         this.name = name;
        22         this.score = score;
        23     }
        24     public String toString() {
        25         return this.name + "," + this.score;
        26     }
        27 }

        運行程式,我們會得到ClassCastException,即無法將Person轉型為Comparable。我們修改程式碼,讓Person實現Comparable<T>介面。

         1 // sort
         2 import java.util.Arrays;
         3 
         4 public class Main {
         5     public static void main(String[] args) {
         6         Person[] ps = new Person[] {
         7             new Person("Bob", 61),
         8             new Person("Alice", 88),
         9             new Person("Lily", 75),
        10         };
        11         Arrays.sort(ps);
        12         System.out.println(Arrays.toString(ps));
        13     }
        14 }
        15 class Person implements Comparable<Person> {
        16     String name;
        17     int score;
        18     Person(String name, int score) {
        19         this.name = name;
        20         this.score = score;
        21     }
        22     public int compareTo(Person other) {
        23         return this.name.compareTo(other.name);
        24     }
        25     public String toString() {
        26         return this.name + "," + this.score;
        27     }
        28 }

        運行上述程式碼,可以正確實現按name進行排序。

      

 

                                      未完待續

Tags: