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..");
}
}