Java泛型中K、T、V、E、?等的含义

  • Post author:
  • Post category:java




1.概述

在定义泛型类、接口和方法时,都会定义一个参数类型,我们用过

<T>



<E>



<K,V>

等,那么这些字母有什么区别和不同呢?

定义Java的泛型时,通常使用的一些类型参数的字母或者符号有:E、T、K、V、N、?Object等。

首先,E、T、K、V、N等这些字母之间没什么区别,使用T的地方完全可以换成U、S、Z等任意字母。当然,一般我们会使用一些常用的字母,这些字符一般是一些类型的缩写。

例如:

  • E : Element的缩写,一般在集合中使用,表示集合中的元素类型。
  • T : Type的缩写,一般表示Java类。
  • K : Key的缩写,一般用来表示“键”,如Map种的key。
  • V : Value的缩写,一般用来表示“值”与K是一对。
  • N : Number的缩写,通常用来表示数值类型。

以上这些类型其实都是确定的类型,如

List<T>

表示List中的类型只能是T。

除此之外,还有不确定的类型,那就是

?



<?>

表示不确定的Java类型,

<?>

也经常出现在集合类中。

需要注意的是,在Java集合框架中,对于参数值是位置类型的容器类,只能读取其中的元素不能向其中添加元素。因为其类型是未知的,所以编译器无法识别添加元素的类型和容器的类型是否兼容,唯一的例外是

null

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

list.add(null); //编译通过

list.add(“Hollis”) // 编译失败


List<?>

是一个未知类型的

List

,不能向

List<?>

中添加元素,但可以把

List<String>



List<Integer>

赋值给

List<?>



在这里插入图片描述

在这里插入图片描述

很多人认为

List<?>



List<Object >

是一样的,其实这是不对的,

<Object>

表示任意类型,

<?>

表示未知类型,可以向

List<Object>

中添加元素,但是不能把

List<String>

赋值给

List<Object>



2.泛型中的限定通配符合非限定通配符

假设你需要一个List来存放Fruits,那么你会定义

List<Fruit> fruits

,你能直接把

List<Apple>

赋值给fruits吗(Apple 继承自 Fruit)?


不能


在这里插入图片描述

以上代码编译失败的原因是

List<Fruit>

中允许添加任何水果,而

List<Apple>

中只允许添加

apple

,这意味着两种类型不兼容。


如果我们只关心List包含某种类型的水果这一事实,那么我们就可以使用类型通配符来定义它

public class Demo {
    public static void main(String[] args) {
        List<Apple> apples = new ArrayList<Apple>();
        List<? extends Fruits> fruits = apples;
    }
}

使用

List<? extends Fruits>

定义的List是可以接收

List<Apple>

的,通过这种形式表名这是一个Fruit或者它的子类List么这意味着列表中的每个元素都是某种水果。

但是我们不能直接向

List<? extends Fruits> fruits

中添加元素:

在这里插入图片描述

因为上面代码定义的List可能是

List<Apple>



Fruit

的其他子类

List



3. 限定通配符与非限定通配符



<? extends Fruits>

这种形式,我们称之为通配符。Java泛型中有两种

限定通配符




一种是

<? extends T>

,保证泛型类型必须是T的子类型来设定泛型类型的上边界,即泛型类型必须为T类型或者T的子类。

public class Demo {
    public static void main(String[] args) {
        List<Apple> apples = new ArrayList<Apple>();
        List<? extends Fruits> fruits = apples;
    }
}

另一种是

<? super T>

保证泛型类型必须是T的父类来设定类型的下边界,即类型必须是T类型或者T的父类。

public class Demo {
    public static void main(String[] args) {
        List<Fruits> fruits = new ArrayList<>();
        List<? super Apple> apples = fruits;
    }
}


<?>

是非限定通配符,表示可以用任意泛型来替代它,即可以把任意类型的List赋值给

List<?>

public class Demo {
    public static void main(String[] args) {
        List<Apple> apples = new ArrayList<Apple>();
        List<Anything> anythings = new ArrayList<>();
        List<?> fruits = apples;
        List<?> fruits = anythings;
    }
}



4.泛型的PECS原则

前面介绍了两个

限定通配符


<? extends T>



<? super T>

,这两个通配符在什么时候使用,使用时又该如何选择呢?

这就不得不提到一个原则–PECS原则,即Producer Extens Consumer Super,这是在集合中使用限定通配符的一个原则。

如果只是从一个泛型集合中提取元素,那么它是一个生成器(Producer),应该使用Extends:

List<? extends Fruits> fruits = new ArrayList<>();
fruits.add(new Apple()) //编译失败

当我们尝试向一个生成器中添加元素时,会编译失败。这是因为编译器只知道这个List中的元素是Fruit及其子类,但具体是那种类型编译器是不知道的。


如果是向集合中添加元素,那么它是一个消费者(Consumer),应该使用Super:


在这里插入图片描述

当我们尝试从消费者中提取元素时,也会编译失败。这是因为编译器只知道这个List中的元素是Apple及其父类,具体是那种类型的编译器是不知道的。

简单地说,在集合中,频繁地往外

读取

内容的场景,适用于

<? extends T>

;经常向集合中插入的场景适合于

<? super T>


另外,如果想在同一个集合中同时使用这两种方法,则不该使用Extends或Super。



版权声明:本文为Blue_Pepsi_Cola原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。