天天看點

[Java 學習筆記] 泛型

目錄

​​ 泛型的向上轉型​​

​​将靜态方法的泛型類型和執行個體類型的泛型類型區分開​​

​​多泛型類​​

​​java可以建立泛型數組(待完善)​​

​​Java實作泛型的方法——擦拭法​​

​​由此,Java泛型的局限也展現出來​​

​​泛型繼承(loading)​​

​​通配符(loading)​​

​​泛型與反射(loading)​​

 泛型的向上轉型

在Java标準庫中,  ArrayList<T>實作了List<T>接口,它可以向上轉型為List<T>:

public class ArrayList<T> implements List<T> {
    ...
}

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

編譯器看到泛型類型List<String>就可以自動推斷出後面的ArrayList<T>的泛型類型必須是ArrayList<String>,代碼可以簡寫為:

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

!注意:不能把​

​ArrayList<Integer>​

​​向上轉型為​

​ArrayList<Number>​

​​或​

​List<Number>​

​。

👇

假設​

​ArrayList<Integer>​

​​向上轉型為​

​ArrayList<Number>​

​:

// 建立ArrayList<Integer>類型:
ArrayList<Integer> integerList = new ArrayList<Integer>();

// 添加一個Integer:
integerList.add(new Integer(123));

// “向上轉型”為ArrayList<Number>:
ArrayList<Number> numberList = integerList;

// 添加一個Float,因為Float也是Number:
numberList.add(new Float(12.34));

// 從ArrayList<Integer>擷取索引為1的元素(即添加的Float):
Integer n = integerList.get(1); // ClassCastException!      
  • 一個ArrayList<Integer>轉型為ArrayList<Number>類型後,這個ArrayList<Number>就可以接受Float類型,因為Float是Number的子類。
  • 而numberList 實際上和integerList 是同一個對象(ArrayList<Integer>類型),不可能接受Float類型, 在擷取Integer的時候将産生ClassCastException。

總結:

編譯器為了避免這種錯誤,根本就不允許把ArrayList<Integer>轉型為ArrayList<Number>。

!注意泛型的繼承關系:可以把ArrayList<Integer>向上轉型為List<Integer>(T不能變),但不能把ArrayList<Integer>向上轉型為ArrayList<Number>(T不能變成父類)

▲另外須知: ArrayList<Integer>和ArrayList<Number>兩者完全沒有繼承關系。

将靜态方法的泛型類型和執行個體類型的泛型類型區分開

注意,泛型類型不能用于靜态方法

下面的代碼編譯錯誤

public class Pair<T> {
    private T first;
    private T last;
    public Pair(T first, T last) {
        this.first = first;
        this.last = last;
    }
    public T getFirst() { ... }
    public T getLast() { ... }

    // 對靜态方法使用<T>:
    public static Pair<T> create(T first, T last) {
        return new Pair<T>(first, last);
    }
}      

 👇正确表示如下

public class Pair<T> {
    private T first;
    private T last;
    public Pair(T first, T last) {
        this.first = first;
        this.last = last;
    }
    public T getFirst() { ... }
    public T getLast() { ... }

    // 靜态泛型方法應該使用其他類型區分:
    public static <K> Pair<K> create(K first, K last) {
        return new Pair<K>(first, last);
    }
}      

多泛型類

class Pair<T,K>
{
    private T first;
    private K second;

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

    public T getFirst(){
        return this.first;
    }
    public K getSecond(){
        return this.second;
    }
}
public class pairx {
    public static void main(String[] args) {
        Pair<String, Integer> p1 = new Pair<>("泥煙", 8080);
        System.out.println(p1.getFirst()+p1.getSecond());
    }
}      

運作結果:

輸出→ 泥煙8080 

另外java是可以建立泛型數組的(還未完全掌握,之後完善):

java可以建立泛型數組(待完善)

Java實作泛型的方法——擦拭法

Java的泛型是由編譯器在編譯時實行的,編譯器内部永遠把所有類型​

​T​

​視為​

​Object​

​處理,但是,在需要轉型的時候,編譯器會根據​

​T​

​的類型自動為我們實行安全地強制轉型。

由此,Java泛型的局限也展現出來

局限一:​

​<T>​

​​不能是基本類型,例如​

​int​

​​,因為實際類型是​

​Object​

​,​

​Object​

​類型無法持有基本類型:
Pair<int> p = new Pair<>(1, 2); // compile error!      
 局限二:無法取得帶泛型的​

​Class​

有如下類

class Pair<T> {
    private T first;
    private T last;
    public Pair(T first, T last) {
        this.first = first;
        this.last = last;
    }
    public T getFirst() {
        return first;
    }
    public T getLast() {
        return last;
    }
}      
Pair<String> p1 = new Pair<>("Hello", "world");
    Pair<Integer> p2 = new Pair<>(123, 456);
    Class c1 = p1.getClass();
    Class c2 = p2.getClass();
    System.out.println(c1==c2); // true
    System.out.println(c1==Pair.class); // true      
無論​

​T​

​​的類型是什麼,​

​getClass()​

​​傳回同一個​

​Class​

​​執行個體,因為編譯後它們全部都是​

​Pair<Object>​

​ 

局限三:無法判斷帶泛型的類型,原因同上

Pair<Integer> p = new Pair<>(123, 456);
// Compile error:
if (p instanceof Pair<String>) {
    ...
}      

并不存在Pair<String>.class,而是隻有唯一的Pair.class

局限四:不能直接執行個體化​

​T​

​類型 
public class Pair<T> {
    private T first;
    private T last;
    public Pair() {
        // Compile error:
        first = new T();
        last = new T();
    }
}



擦拭後實際上變成了:

first = new Object();
last = new Object();      
将其執行個體化的方法

泛型繼承(loading)

通配符(loading)

泛型與反射(loading)