泛型,廣泛的類型


其實早在1999年的JSR 14規范中就提到了泛型概念,知道jdk5泛型的使用才正式發布,在jdk7后,又對泛型做了優化,泛型的推斷.

泛型類

public class Pair<T> {
    private T first;
    private T second;

    public Pair() {
        first = null;
        second = null;
    }

    public Pair(T first, T second) {
        this.second = second;
        this.first = first;
    }

    public void setFirst(T newValue) {
        first = newValue;
    }

    public void setSecond(T newValue) {
        second = newValue;
    }
}

怎么理解泛型類的定義.首先在類名后根上<T>這個T就是任意類型.在 Java 庫中, 使用變量 E 表示集合的元素類型, K 和 V 分別表示表的關鍵字與值的類型。T ( 需要時還可以用臨近的字母 U 和 S) 表示“ 任意類型”。然后在類中的成員,都可以使用這個T,你既可以把T當做參數,也可以把T當做返回值.也可以把T當做成員變量的類型.這個T到底存儲的什么類型,取決於你在實例化Pair時指定的具體類型.但是以上寫法,你一旦指定了一個實際類型,那么這個類中所有的T都會是同一個類型.

public class Main {
    
    public static void main(String[] args) {
        Pair<String> pair = new Pair<>();
        pair.setFirst("第一");
        pair.setSecond("第二");
    }
}

你也可以在一個泛型類上定義多個泛型

public class Pair<T,U> {
    private T first;
    private U second;

    public Pair() {
        first = null;
        second = null;
    }

    public Pair(T first, U second) {
        this.second = second;
        this.first = first;
    }

    public void setFirst(T newValue) {
        first = newValue;
    }

    public void setSecond(U newValue) {
        second = newValue;
    }
}

但是你需要記得,因為泛型的作用域在類級別.一下寫法是錯誤的.

 

 

 你在類上定義了個T表示,你所實例化的每一個類都要指定一個類型,現在,現在你試圖不做類的實例化,而直接使用T,那么這個T你要從哪里定義呢?記住要使用泛型,先確定泛型的具體類型.

泛型方法

public class Demo3 {
    public   <T> void show(T t){
        System.out.println(t.toString());
    }
    public static  <S> void show2(S s){
        System.out.println(s);
    }
public class Main {
    public static void main(String[] args) {
        Demo3 demo3 = new Demo3();
        demo3.<String>show("a");
        demo3.show("a");
        Demo3.show2(1);
    }
}

你在一個方法上指定了泛型,即泛型的作用域在方法體上,也就是說,你每次調用方法都要指定具體的類型.當然你不用每次調用都使用<T>語法,因為jdk7的泛型推斷.編譯器自然可以通過你的實參而推斷出你想要的實際類型.將泛型定義到方法上,泛型的T可以用到參數,方法體,返回值.

當然在你指定泛型時,也可以有以下寫法

public class Demo1 {
    public  <String> void add(String t){
    }
}

不過這通常是沒有任何意義的,否則,你要想表達什么呢?定義了一個泛型方法,並且限定泛型的實際類型是String?

泛型的擦除

泛型的擦除可謂是泛型中的重中之重了.字面意思,泛型類型會被擦除.引起兩個問題:1在什么情況下擦除2被擦除后的的類什么樣.

文檔說明泛型只在編譯器用來檢測類型,編譯時即會擦除泛型.所以泛型是在編譯時被擦除的.下面看一下代碼

public class Demo4<T> {
    private T type;

    public void add(T t) {
    }
}

 

 

 可以看出在無限定類型時(沒有使用extends 或 super 限定泛型)在編譯后原來的T被替換成了Object.

 

泛型表達式

看以下代碼

public class Pair<T> {
    private T first;
    private T second;

    public Pair() {
        first = null;
        second = null;
    }

    public Pair(T first, T second) {
        this.second = second;
        this.first = first;
    }

    public T getFirst() {
        return first;
    }

    public void setFirst(T first) {
        this.first = first;
    }

    public T getSecond() {
        return second;
    }

    public void setSecond(T second) {
        this.second = second;
    }
}
public class Main {
    public static void main(String[] args) {
        Pair<Student> pair = new Pair<>();
        Student s = pair.getFirst();
    }
}

前邊已經說過,對於無限定類型,在編譯時會擦掉泛型的而變成object.那么以上這個Main中運行的代碼,pair通過get()方法的返回值確可以直接賦值給Student這又是怎么回事呢?

 

 

 

 

 

 這是兩個class反編譯后的.可以看到,在get()方法后,編譯器幫我們自動做了類型轉換

泛型與多態的沖突(橋方法)

public class Pair<T> {
    private T first;
    private T second;

    public Pair() {
        first = null;
        second = null;
    }

    public Pair(T first, T second) {
        this.second = second;
        this.first = first;
    }

    public T getFirst() {
        return first;
    }

    public void setFirst(T first) {
        this.first = first;
    }

    public T getSecond() {
        return second;
    }

    public void setSecond(T second) {
        this.second = second;
    }
}

 

public class PairChild extends Pair<Person> {
    @Override
    public void setFirst(Person first) {
        super.setFirst(first);
    }
}

我們繼承了Pair並且指定了他的具體類型.那么我們覆蓋Pair方法時就只能傳入Person類型的參數了.那么,在已經編譯好的Pair.class中,setFirst()應該還是Object類型.這時我們到底算是覆蓋父類的方法了嗎?

Pair.class

 

 

 PairChild.class

 

 

其中object參數的方法就是橋方法,當我們使用setFirst時,會先調用這個橋方法,這個橋方法,會將object強制轉換成Person然后在調用PairChild自己的setFirst().

打破泛型的約束

有時間泛型對待數據類型也不是絕對安全的.請看一下示例

public class Pair<T> {
    private T first;
    private T second;

    public Pair() {
        first = null;
        second = null;
    }

    public Pair(T first, T second) {
        this.second = second;
        this.first = first;
    }

    public T getFirst() {
        return first;
    }

    public void setFirst(T first) {
        this.first = first;
    }

    public T getSecond() {
        return second;
    }

    public void setSecond(T second) {
        this.second = second;
    }
}
public class PairSort  {
    public void sort(Pair pair){
        pair.setFirst("可是我是字符串");
    }
}
public class Main {
    public static void main(String[] args) {
        PairSort pairSort = new PairSort();
        Pair<Person> pair = new Pair();
        pairSort.sort(pair);
        Person first = pair.getFirst();
    }
}

我們有一個PairSort類,這個類接受一個Pair實例,但是並沒有指定泛型,也就是說,它現在接受的是一個Object.然后給他的卻是一個指定了Person類型的Pair.這時我們在調用Pair就獲得了一個錯誤.

 

 約束與限制

1 不能使用基本類型實例化類型參數,我們不能傳遞基本數據類型當做泛型的具體類型,原因是當泛型擦除后需要轉換成具體的object子類.而基本數據類型,並不能轉換成object

2 檢查一個對象的類型不能帶泛型參數.看一下代碼

 

 

 3 不能創建參數化類型的數組

 

 

 4 不能實例化類型變量

 

 5 盡管有泛型的擦除,但是在靜態中依然不能使用泛型

 

 6 泛型類中不能覆蓋父類的方法

通配符

請看以下錯誤代碼

public class Pair<T> {
    private T first;
    private T second;

    public Pair() {
        first = null;
        second = null;
    }

    public Pair(T first, T second) {
        this.second = second;
        this.first = first;
    }

    public T getFirst() {
        return first;
    }

    public void setFirst(T first) {
        this.first = first;
    }

    public T getSecond() {
        return second;
    }

    public void setSecond(T second) {
        this.second = second;
    }
}
public class Employee {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
public class Manager extends Employee {
}
public class Main {

    public static void main(String[] args) {
        Pair<Manager> pair = new Pair<>();
        printBuddies(pair);//錯誤的
    }

    public static void printBuddies(Pair<Employee> p) {
        Employee first = p.getFirst();
        Employee second = p.getSecond();
        System.out.println(first.getName());
    }
}
printBuddies()需要一個Pair類型參數,我們指定Pair的泛型類型是Employee.當我們傳遞實參時,傳遞的是Pair<Manager>類型的實參,這是不正確的.Pair<Employee>和Pair<Manager>沒有父子關系.
他們都只是Pair類型.那么對於這種問題有沒有解決方案的?
public class Main {

    public static void main(String[] args) {
        Pair<Manager> pair = new Pair<>();
        printBuddies(pair);
        
        Pair<Employee> employeePair = new Pair<>();
        printBuddies(employeePair);
    }

    public static void printBuddies(Pair<? extends Employee> p) {
        Employee first = p.getFirst();
        Employee second = p.getSecond();
        System.out.println(first.getName());
    }
}

使用 ? extends Employee 指定Pair的泛型類型是Employee或者是其子類.這樣就不會出現編譯異常

看一下對通配符的使用

public class Main {
    public static void main(String[] args) {
        Pair<Manager> pair  = new Pair<>();
        Pair<? extends Employee> pair1 = pair;
        pair.setFirst(new Employee());//錯誤的
        pair.setFirst(new Manager());
        Employee first = pair.getFirst();
    }
}

 

我們創建一個Manager類型的Pair.將他賦值給Pair<Employee>這是沒錯的.但是當我們調用set方法則會出現編譯異常.原因是編譯器知道我們要傳入Employee的子類型但是不知道具體傳入的是哪個子類型所以

拒絕編譯.但是get方法則沒有問題,因為返回是一個Employee

超類通配符

public class Main {

    public static void main(String[] args) {
        printBuddies(new Pair<Employee>());
        printBuddies(new Pair<Manager>());
    }

    public static void printBuddies(Pair<? super Manager> p) {
        Object first = p.getFirst();
        Object second = p.getSecond();
    }
}

我們限定printBuddies的參數為Manager或者其父類.

 

 

 由於我們希望傳入Manager或者其父類所以get方法拒絕我們用一個Manager接收.它無法確定我們到底傳入的是Manager還是其父類所以只能用Object接收.

 

無限定通配符

 

 

 

 

 

 雖是無限定通配符,但是他的使用限定是最大的.我們甚至無法使用set方法.除非傳遞一個null.Object都不行.在get方法時我們也只能用Object來接收.那么為什么要有這樣一個雞肋的通配符呢?


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM