1. 程式人生 > >JAVA快速入門-泛型

JAVA快速入門-泛型

Tips:

泛型是程式設計語言的一種特性。允許程式設計師在強型別程式設計語言中編寫程式碼時定義一些可變部分,那些部分在使用前必須作出指明。各種程式設計語言和其編譯器、執行環境對泛型的支援均不一樣。將型別引數化以達到程式碼複用提高軟體開發工作效率的一種資料型別。泛型類是引用型別,是堆物件,主要是引入了型別引數這個概念。
泛型的定義主要有以下兩種:
1.在程式編碼中一些包含型別引數的型別,也就是說泛型的引數只可以代表類,不能代表個別物件。(這是當今較常見的定義)
2.在程式編碼中一些包含引數的類。其引數可以代表類或物件等等。(人們大多把這稱作模板)不論使用哪個定義,泛型的引數在真正使用泛型時都必須作出指明。
一些強型別程式語言支援泛型,其主要目的是加強型別安全及減少類轉換的次數,但一些支援泛型的程式語言只能達到部分目的。

上面Tips如果是不瞭解泛型的還是比較難理解,本章使用程式碼的形式,可以簡潔明瞭的看出泛型究竟是什麼,下面先寫一個沒有使用到泛型的JAVA程式碼,如下:

class Person {
    private int age;

    public void setAge(int age) {
        this.age  = age;
    }

    public int getAge() {
        return this.age;
    }
}

public class Generics {
    public static void main(String args[]) {
        Person p = new
Person(); p.setAge(3); System.out.println(p.getAge()); } }

上面的程式碼,假設我們 setAge 這個函式要傳遞的引數不是整型變量了,要傳遞個 “3 years old” 這個字串,那麼我們需要把 Person 類裡面的定義的 int 型變數全都一個一個的改成 String,這顯然就很不方便也很浪費時間,那麼這時候就可以引入泛型了,泛型就是把類裡面的屬性型別使用名字來代替,當我們需要把 int 改為 String 的時候,直接修改定義一處即可實現所有型別的替換,還是以程式碼的形式比較直觀,經過修改上面的程式碼使用泛型後,如下:

/* 這個 T 就是泛型(可以用其它名字,不一定是T),它可以替代類裡面使用到的變數型別 */
class Person <T>{
    private T age;

    public void setAge(T age) {
        this.age  = age;
    }

    public T getAge() {
        return this.age;
    }
}

public class Generics {
    public static void main(String args[]) {
        /* 當我們需要把int改為String,直接往<>裡面寫入型別即可 */
        Person<String> p = new Person<String>();
        p.setAge("3 years old");
        System.out.println(p.getAge());

        /* 這裡不能用int,因為泛型是類型別,也需要使用Integer類 */
        Person<Integer> p2 = new Person<Integer>();
        p2.setAge(3);
        System.out.println(p2.getAge());
    }
}

當然,上面的 < T > 如果想宣告成使用兩個泛型的話也是可以的,比如 < T,N > 來代替兩個型別,在定義時也需要定義兩個型別,比如< String,int >。上面的列印過程如果使用函式來實現,那函式形參的定義能不能也使用泛型呢,上面傳遞的類他們的泛型都不一樣,一個是< String >,一個是< Integer >,那我們在傳遞給形參的時候可以使用萬用字元< ? >來定義,也可以把其它已經例項化的物件賦值給萬用字元的物件,如下程式碼所示:

class Person <T>{
    private T age;

    public void setAge(T age) {
        this.age  = age;
    }

    public T getAge() {
        return this.age;
    }
}

public class Generics {
    public static void main(String args[]) {
        Person<String> p = new Person<String>();
        p.setAge("3 years old");
        //System.out.println(p.getAge());
        printInfo(p);

        Person<Integer> p2 = new Person<Integer>();
        p2.setAge(3);
        //System.out.println(p2.getAge());
        printInfo(p2);

        /* 使用萬用字元來特定一個物件的時候,只能去獲取,無法設定,就是沒法setAge */
        Person<?> p3;
        p3 = p;
        p3.getAge();
    }

    /* <?> 使用這個萬用字元表示代替傳遞進來的型別 */    
    public static void printInfo(Person<?> p) {
        System.out.println(p.getAge());
    }
}

那麼如果想讓形參也使用泛型不使用萬用字元< ? >行不行呢,可以的,看程式碼的實現:

public class Generics {
    public static void main(String args[]) {
        Person<String> p = new Person<String>();
        p.setAge("3 years old");
        //System.out.println(p.getAge());
        printInfo(p);

        Person<Integer> p2 = new Person<Integer>();
        p2.setAge(3);
        //System.out.println(p2.getAge());
        printInfo(p2);


        Person<?> p3;
        p3 = p;
        p3.getAge();

        /* 形參為泛型的使用 */
        printInfo2(p);
        printInfo2(p2);
        printInfo2(p3);
    }

    public static void printInfo(Person<?> p) {
        System.out.println(p.getAge());
    }

    /* 前面的<T>用來表示形參中的引數是泛型 */
    public static <T> void printInfo2(Person<T> p) {
        System.out.println(p.getAge());
    }
}

子類能否也使用父類裡面的泛型呢,具體實現看程式碼:

class Person <T>{
    private T age;

    public void setAge(T age) {
        this.age  = age;
    }

    public T getAge() {
        return this.age;
    }
}

/* 子類使用泛型 */
class Student<T> extends Person<T> {
}

/* 子類不想使用泛型了,就要指定使用哪一種泛型,如下 */
class Student2 extends Person<String> {
}

public class Generics {
    public static void main(String args[]) {
        Person<String> p = new Person<String>();
        p.setAge("3 years old");
        //System.out.println(p.getAge());
        printInfo(p);

        Person<Integer> p2 = new Person<Integer>();
        p2.setAge(3);
        //System.out.println(p2.getAge());
        printInfo(p2);


        Person<?> p3;
        p3 = p;
        p3.getAge();

        /* 形參為泛型的使用 */
        printInfo2(p);
        printInfo2(p2);
        printInfo2(p3);

        Student<Integer> s = new Student<Integer>();
        s.setAge(10);
        printInfo(s);//傳入s子類會向上轉換為Person父類

        /* 子類不使用泛型,只有父類使用的話定義的時候這個子類就不用指定泛型了 */
        Student2 s2 = new Student2();
        s2.setAge("11 years old");
        printInfo(s2);
    }

    public static void printInfo(Person<?> p) {
        System.out.println(p.getAge());
    }

    /* 前面的<T>用來表示形參中的引數是泛型 */
    public static <T> void printInfo2(Person<T> p) {
        System.out.println(p.getAge());
    }
}

當然,介面可以看做是特殊的父類,當然也能使用泛型,如下:

interface Person <T>{
    public void setAge(T age);
    public T getAge();
}

class Student<T> implements Person<T> {
    T age;

    public void setAge(T age){
        this.age = age;
    }
    public T getAge(){
        return this.age;
    }
}


class Student2 implements Person<String> {
    String age;

    public void setAge(String age){
        this.age = age;
    }
    public String getAge(){
        return this.age;
    }
}

public class Generics {
    public static void main(String args[]) {
        Student<Integer> s = new Student<Integer>();
        s.setAge(10);
        printInfo(s);//傳入s會向上轉換了Person

        /* 定義的時候這個子類就不用指定泛型了 */
        Student2 s2 = new Student2();
        s2.setAge("11 years old");
        printInfo(s2);
    }

    public static void printInfo(Person<?> p) {
        System.out.println(p.getAge());
    }

    /* 前面的<T>用來表示形參中的引數是泛型 */
    public static <T> void printInfo2(Person<T> p) {
        System.out.println(p.getAge());
    }
}

最後再看看“受限的泛型”,泛型可以定義上限和下限,先來看看泛型的上限,把 T 定義為只能是 Number 類或其子類,用 extemds 表示上限。

interface Person <T>{
    public void setAge(T age);
    public T getAge();
}

/* 指定只能是 Integer 或 float等 */
/* 這些類都來源於 Number 類 */
class Student<T extends Number> implements Person<T> {
    T age;

    public void setAge(T age){
        this.age = age;
    }
    public T getAge(){
        return this.age;
    }
}


class Student2 implements Person<String> {
    String age;

    public void setAge(String age){
        this.age = age;
    }
    public String getAge(){
        return this.age;
    }
}

public class Generics {
    public static void main(String args[]) {
        /* 加了上限後如果這裡的泛型使用String就會出錯 */
        Student<Integer> s = new Student<Integer>();
        s.setAge(10);
        printInfo(s);

        Student2 s2 = new Student2();
        s2.setAge("11 years old");
        printInfo(s2);
    }

    public static void printInfo(Person<?> p) {
        System.out.println(p.getAge());
    }
    public static <T> void printInfo2(Person<T> p) {
        System.out.println(p.getAge());
    }
}

再看看泛型的下限,把 T 定義為只能是 String 類或其父類,用 super 表示下限,而且super只能用於萬用字元,如下:

interface Person <T>{
    public void setAge(T age);
    public T getAge();
}

//不能直接這樣用,super只能用於萬用字元
//class Student<T super String> implements Person<T> {
class Student<T> implements Person<T> {
    T age;

    public void setAge(T age){
        this.age = age;
    }
    public T getAge(){
        return this.age;
    }
}

class Student2 implements Person<String> {
    String age;

    public void setAge(String age){
        this.age = age;
    }
    public String getAge(){
        return this.age;
    }
}

public class Generics {
    public static void main(String args[]) {
        /* 這裡因為有下限所以要改成String才能編譯通過 */
        Student<String> s = new Student<String>();
        s.setAge("10 years old");
        printInfo(s);

        Student2 s2 = new Student2();
        s2.setAge("11 years old");
        printInfo(s2);
    }

    /* 只允許那些萬用字元是String或者String父類的例項化物件 */
    public static void printInfo(Person<? super String> p) {
        System.out.println(p.getAge());
    }

    public static <T> void printInfo2(Person<T> p) {
        System.out.println(p.getAge());
    }
}