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..");
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章