泛型

      在〈泛型〉中尚無留言

List

陣列 (Array) 是一個長度固定的資料結構,如果要儲存的資料數量無法事先預測有多少,陣列就派不上用場。此時就需要一個能隨時變換長度的資料結構,List 就是依此需求而產生。

List 是一個抽象類別,規範存放資料的方法,存放的資料都是物件型態,如果放入的資料是八大原生資料,會自動轉成 Integer、Float 等物件型態。也就是說 List 什麼資料都可以存放,是一個很髒的百寶袋。

因為 List 是抽象類別,不可以直接產生物件。而 ArrayList 則是實作 List 的類別,所以需使用 ArrayList 存放資料。不過 ArrayList 並不是雙向連結,每當新增刪除資料時,需進行大量的搬移,所以效能非常差。

package net.ddns.mahaljsp;

import java.util.ArrayList;
import java.util.List;

public class Test {
    public static void main(String[] args) {
        G.encoding();
        List list=new ArrayList();
        list.add("Sunday");
        list.add("Monday");
        list.add("Tuesday");
        list.add("Wednesday");
        list.add("Thursday");
        list.add("Friday");
        list.add("Saturday");
        
        list.add(10);

        for (Object o:list){
            System.out.println(o.toString());
        }
    }
}

上述代碼什麼資料都可以存放,最後取出時是使用 Object 型態。

泛型(Generics)

泛型又稱廣泛的類型,透過型別的參數化來達成。使用泛型可以減少重複撰寫程式碼,用如下例子說明。

class Pikachu{}
class Eve{}

//末使用泛型
class PikachuGroup{
    private Pikachu[] array;
    public Pikachu[] getArray(){
        return array;
    }
}
class EveGroup{
    private Eve[] array;
    public Eve[] getArray(){
        return array;
    }
}

//上述二個xxxGroup, 可以改寫成如下一個
//使用泛型
class Group<T>{
    private T[] array;
    public T[] getArray(){
        return array;
    }
}

而在 main 裏可以寫成如下

public class JavaApplication1 {
    public static void main(String[] args) {
        PikachuGroup pg1 = new PikachuGroup();
        Group<Pikachu> pg2 = new Group<>();
        Pikachu[] array1 = pg1.getArray();//返回 Pikachu[] 的型態
        Pikachu[] array2 = pg2.getArray();//同樣是返回 Pikachu[] 的型態

        Group<Eve> eg3 = new Group<>();
        Eve[] array3 = eg3.getArray();//此時返回Eve[]的型態
    }
}

pg2 及 eg3 同樣是 Group 的型態,但 pg2.getArray() 及 eg3.getArray() 卻傳回不同的型態。

泛化(Generalization)

List 可以存放任何型態的資料,非常雜亂。底下代碼藍色部份會導致執行後發生 RuntimeError 而閃退。

    public class GenericsTest {
        public static void main(String[] args) {
            List list=new ArrayList();
            list.add("Thomas");
            list.add("John");
            list.add("Merry");
            list.add(new Integer(10));//不會錯
            for (Object o:list){
                String d=(String)o;//轉到Integer時發生RuntimeError
                System.out.println(d);
            }
        }
    }

如果能強制規定 List 只能存放一種資料型別,就不會發生 RuntimeError 的閃退問題。規定只能存放特定型別的功能,稱為泛化 (Generalization)。下面的代碼指定只能存入 String。

    public class GenericsTest {
        public static void main(String[] args) {
            List<String> list=new ArrayList<>();
            list.add("Thomas");
            list.add("John");
            list.add("Merry");
            list.add(new Integer(100));//Compile error
            for (String o:list){
                System.out.println(o);
            }
        }
    }

泛化並不是在 Runtime 時期處理,而是在編譯時期就開始檢查,所以上述紅色那行可以很輕易的在編譯期就找出錯誤。

泛化一詞是指在編譯時期就產生一個專屬的類別,產生此專屬類別這個動作稱為泛化。

Vector 類別

Vector 是 C++ 語言唯一的 List 類別。早期的 Java 模仿 C++ 也使用 Vector,但後來被放棄了。如果使用如下

 Vector v=new Vector();

編譯器會提出警告–Obsolete collection。阿諾的名言~~I am old, but not obsolete.

參數化

泛型變數 <T> 代表任何型態. 任何變數名稱都可以使用, 不是一定要用<T>, 但通常有不成文的規定如下
T : type
E: element
K: Key
V:Value
S, U第二第三型態

Diamond <>

因為 OCP 把 <> 稱為 Diamond(鑽石),所以這個名詞要記一下。

泛化精簡

早期 Java 6.0 及之前的版本,規定一定要寫成如下

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

這種寫法左邊要 String,右邊也要 String,是種蠻煩人的設計。所以到了 Java 7.0 之後,改良為如下。

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

泛型與原生資料

泛型不能接收原生基本資料,所以只能將原生資料包裝成類別。

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

Iterator 與泛型

Iterator 本身也支援泛型

package net.ddns.mahaljsp;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class Test {
    public static void main(String[] args) {
        G.encoding();
        List<String> list=new ArrayList<>();
        list.add("Sunday");
        list.add("Monday");
        list.add("Tuesday");
        list.add("Wednesday");
        list.add("Thursday");
        list.add("Friday");
        list.add("Saturday");
        
        Iterator<String> it=list.iterator();
        while(it.hasNext()){
            System.out.println(it.next());
        }
    }
}

方法回傳值的泛化

如下例子, 方法的傳回值也是泛化的

    public static List<String> getList(){
        List list=new ArrayList<>();
        list.add("Thomas");
        list.add("John");
        list.add("Merry");
        return list;
    }

泛型多型

泛型多型的轉換,只允許基底型別的轉換,參數型別必需是一模一樣的。
Vector<Integer> v1=new Vector<>();
List<Integer> v2=v1; //ok

Vector<Integer> v1=new Vector<>();
Vector<Number>v2=v1; //編譯錯誤

泛型萬用型別<?>

方法會接受來自不同的呼叫者,而每個呼叫者的型態都不一樣時,此時就可以使用 <?>。

public class GenericsTest {
    public static void main(String[] args) {
        List<String> list1=new ArrayList<>();
        list1.add("Thomas");
        list1.add("John");
        list1.add("Merry");
        List<Integer> list2=new ArrayList<>();
        list2.add(5);
        list2.add(10);
        list2.add(15);
        printList(list1);
        printList(list2);
    }
    public static void printList(List<?> list){
        Iterator<?> it=list.iterator();
        while(it.hasNext()){
            System.out.println(it.next());
        }
    }
}

上述其實不需要 <?>,可以改成如下

public static void printList(List list){
    ............
}

限制參數的型別

如果寫成如下,就只有 Integer,Float,Double 等數字才可傳入。

public static void printList(List<? extends Number>list){
    .....
}

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *