Java通配符和類型參數的使用(?和T的區別)

1、通配符類型(?)

通配符類型可以理解爲一種泛型調用時傳遞的一種特殊數據類型,表示參數允許在某個範圍內變化。通配符類型有三種,分別是?,? extends,? super。
通配符表示一種未知類型,並且對這種未知類型存在約束關係.
<?>
?的默認是實現是? extends Object,表示?是繼承Object的任意類型。

<? extends T> 上限通配
這裏?表示一個未知的類,而T是一個具體的類,在實際使用的時候T需要替換成一個具體的類,表示實例化的時候泛型參數要是T或T的子類。
例如:

//定義方法
public static void function1(List<? extends Number> list){ 
    System.out.println(list.get(0).toString()); 
}
//使用方法
List<Integer> list1 = new ArrayList<>();
list.add(1);
List<Number> list2 = new ArrayList<>();
list2.add(2);
Demo.function1(list);
Demo.function1(list2);

<? super T> 下限通配
這裏?表示一個未知的類,而T是一個具體的類,在實際使用的時候T需要替換成一個具體的類,表示實例化的時候泛型參數要是T或是T的父類。
例如:

//定義方法
public static void function2(List<? super Integer> list){ 
    System.out.println(list.get(0).toString()); 
}
//使用方法
List<Integer> list = new ArrayList<>();
list.add(1);
List<Number> list2 = new ArrayList<>();
list2.add(2);
Demo.function2(list);
Demo.function2(list2);

PECS(producer-extends,consumer-super)
Effective Java中提出的一種概念;生成者使用<? extends T>,消費者使用<? super T>。

下面例子中,T爲Number類型,這裏的src裏面的對象都是Number的子類,而接收數據的dest定義的泛型都是Number的父類,所以src中生成的數據存儲到dest不會出現類型異常。

例如:

	/**
     * 生成者使用<? extends T>,消費者使用<? super T>
     * @param src  生產者
     * @param dest 消費者
     * @param <T>
     */
    public static <T> void copy(List<? extends T> src, List<? super T> dest) {
        for (int i = 0; i < src.size(); i++) {
            dest.set(i, src.get(i));
        }
    }

	List<? extends Number> src = new ArrayList<>();
	List<? super Number> dest = new ArrayList<>();
	Demo.copy(src, dest);

2、E、T、K、V、N

本質上以下字母都可以相互替換,但我們按照下面定義約定俗成的含義來使用。

E - Element (在集合中使用,因爲集合中存放的是元素)

T - Type(Java 類)

K - Key(鍵)

V - Value(值)

N - Number(數值類型)

? - 表示不確定的java類型

S、U、V - 2nd、3rd、4th types

在定義泛型時,每個字母都限定代表同一個類型。比如使用T時,會限定多處使用T的指定類型必須要相同。
在下面例子中,src2和dest2的泛型類型必須相同,否則編譯無法通過。
例如:

	//定義T參數的方法
	public static <T> void copy2(List<T> src, List<T> dest) {
        System.out.println("doSomething..");
    }
	
	//正確使用
	List<Number> src2 = new ArrayList<>();
    List<Number> dest2 = new ArrayList<>();
    Demo.copy2(src2, dest2);
		
	//錯誤使用
	List<Number> src2 = new ArrayList<>();
    List<Integer> dest2 = new ArrayList<>();
    Demo.copy2(src2, dest2);

3、? 與 T 的差別

? 表示一個未知類型, T 是表示一個確定的類型。 因此,無法使用 ? 像 T 聲明變量和使用變量。
例如:

	// 正確定義
    static <T> void test1(List<T> list) {
        T t = list.get(0);
        t.toString();
    }
    // 錯誤定義
    static void test2(List<?> list){
        ? t = list.get(0);
        t.toString();
    }

4、附代碼

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

public class Client {
    public static void main(String[] args) {
    	//通配符的上、下限使用
        List<Integer> list1 = new ArrayList<>();
        list1.add(1);
        List<Number> list2 = new ArrayList<>();
        list2.add(2);
        Demo.function1(list1);
        Demo.function2(list1);
        Demo.function1(list2);
        Demo.function2(list2);
		//PECS
        List<? extends Number> src = new ArrayList<>();
        List<? super Number> dest = new ArrayList<>();
        Demo.copy(src, dest);
		
		//T的限定類型 正確使用
        List<Number> src2 = new ArrayList<>();
        List<Number> dest2 = new ArrayList<>();
        Demo.copy2(src2, dest2);
		
		//T的限定類型 錯誤使用,會出現編譯錯誤
        List<Number> src2 = new ArrayList<>();
        List<Integer> dest2 = new ArrayList<>();
        Demo.copy2(src2, dest2);
    }
}
import java.util.List;

public class Demo {
    public static void function1(List<? extends Number> list) {
        System.out.println(list.get(0).toString());
    }

    public static void function2(List<? super Integer> list) {
        System.out.println(list.get(0).toString());
    }

    /**
     * 生成者使用<? extends T>,消費者使用<? super T>
     * @param src  生產者
     * @param dest 消費者
     * @param <T>
     */
    public static <T> void copy(List<? extends T> src, List<? super T> dest) {
        for (int i = 0; i < src.size(); i++) {
            dest.set(i, src.get(i));
        }
    }


    public static <T> void copy2(List<T> src, List<T> dest) {
        System.out.println("doSomething..");
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章