开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第40天,点击查看活动详情
我们在定义泛型类,泛型方法,泛型接口的时候经常会碰见很多不同的通配符,比如 T,E,K,V,? 等等,下面来详细讲一下这些通配符。
常用的通配符
本质上都是通配符没啥区别,只不过是编码时的一种约定俗成的东西(可以说提高了代码可读性)。比如上述代码中的 T ,我们可以换成 A-Z 之间的任何一个大小写字母都可以,并不会影响程序的正常运行,但是如果换成其他的字母代替 T ,在可读性上可能会弱一些。通常情况下,T,E,K,V,? 是这样约定的:
- ? 表示不确定的 java 类型
- T (Type) 表示具体的一个java类型
- K V (Key Value) 分别代表java键值中的Key Value
- E (element) 代表Element
比较难的就是?通配符,下面就着重讲一下
‘ ? ‘无界通配符
基本用法
List<Animal> listAnimals
但是如果用通配符的话:
List<? extends Animal> listAnimals
为什么要使用通配符而不是简单的泛型呢?通配符其实在声明局部变量时是没有什么意义的,但是当你为一个方法声明一个参数时,它是非常重要的。
package keyAndDifficultPoints.Generic;
import java.util.ArrayList;
import java.util.List;
/**
* 功能描述: 泛型通配符测试
*/
public class Test_Wildcard_Character {
public static void main(String[] args) {
List<Dog> dogList = new ArrayList<>();
test(dogList);
test1(dogList);
}
static void test(List<? extends Animal> animals) {
System.out.println("test输出:");
for (Animal animal : animals) {
System.out.print(animal.toString() + "-");
}
}
static void test1(List<Animal> animals) {
System.out.println("test1输出:");
for (Animal animal : animals) {
System.out.print(animal.toString() + "-");
}
}
}
class Animal {
@Override
public String toString() {
return "Animal";
}
}
class Dog extends Animal {
@Override
public String toString() {
return "Dog";
}
}
方法test1()在编译时就会飘红,如下图:
所以,对于不确定或者不关心实际要操作的类型,可以使用无限制通配符(尖括号里一个问号,即 <?> ),表示可以持有任何类型。像 test()方法中,限定了上界,但是不关心具体类型是什么,所以对于传入的 Animal 的所有子类都可以支持,并且不会报错,而test1()就不行。
‘ ? ‘通配符的继承
/*
2. 通配符的使用
通配符:?
类A是类B的父类,G<A>和G<B>是没有关系的,二者共同的父类是:G<?>
*/
@Test
public void test3() {
List<Object> list1 = null;
List<String> list2 = null;
List<?> list = null;
list = list1;
list = list2;
//编译通过
// print(list1);
// print(list2);
//
List<String> list3 = new ArrayList<>();
list3.add("AA");
list3.add("BB");
list3.add("CC");
list = list3;
//添加(写入):对于List<?>就不能向其内部添加数据。
//除了添加null之外。
// list.add("DD");
// list.add('?');
list.add(null);
//获取(读取):允许读取数据,读取的数据类型为Object。
Object o = list.get(0);
System.out.println(o);
}