1. 通配符基本使用
假设有这样的需求:定义一个方法,可以遍历任意数据类型的ArrayList集合。
从前面已经知道,ArrayList 集合和 ArrayList、ArrayList 集合等不存在继承关系,所有方法的形参不能使用ArrayList,那么该如何实现呢?
方式一:不使用泛型:
public class WildcardTest {
public static void main(String[] args) {
ArrayList<String> list1 = new ArrayList<>();
list1.add("aaa");
list1.add("bbb");
list1.add("ccc");
getElement(list1);
ArrayList<Integer> list2 = new ArrayList<>();
list2.add(1);
list2.add(2);
list2.add(3);
getElement(list2);
}
static <E> void getElement(ArrayList list) {
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
Object o = iterator.next();
System.out.println(o);
}
}
}
集合元素都会转为Object类型
方式二:使用泛型方法
public class WildcardTest {
public static void main(String[] args) {
ArrayList<String> list1 = new ArrayList<>();
list1.add("aaa");
list1.add("bbb");
list1.add("ccc");
getElement(list1);
ArrayList<Integer> list2 = new ArrayList<>();
list2.add(1);
list2.add(2);
list2.add(3);
getElement(list2);
}
static <E> void getElement(ArrayList<E> list) {
Iterator<E> iterator = list.iterator();
while (iterator.hasNext()) {
E e = iterator.next();
System.out.println(e);
}
}
}
这种方法需要声明泛型形参 T
方式三:使用类型通配符
public class WildcardTest {
public static void main(String[] args) {
ArrayList<String> list1 = new ArrayList<>();
list1.add("aaa");
list1.add("bbb");
list1.add("ccc");
getElement(list1);
ArrayList<Integer> list2 = new ArrayList<>();
list2.add(1);
list2.add(2);
list2.add(3);
getElement(list2);
}
static void getElement(ArrayList<?> list) {
Iterator<?> iterator = list.iterator();
while (iterator.hasNext()) {
Object o = iterator.next();
System.out.println(o);
}
}
}
为了表示各种泛型List的子类,可以使用类型通配符,类型通配符是一个问号 ?,将一个问号作为类型实参传给List集合,写作:List<?>,List<?>是List<String>
、List<Object>
等各种泛型List的父类。
用List<?> 声明的变量可以接收List集合的任意泛型对象,只要在创建List集合子类对象时,<>中写一个引用类型都是可以的。
List<?> list = new ArrayList<Object>();//合法
list = new ArrayList<Integer>(); //合法
list = new ArrayList<String>(); //合法
一旦使用了类型通配符 ?,只能接收数据不能往该集合中存储数据, 唯一的例外是null
代码示例:
ArrayList<?> list = new ArrayList<String>();
list.add(null);
list.add("aaa"); //编译不通过
使用注意点
- 注意点1:不能用在泛型方法声明上,返回值类型前面<>不能使用?
//编译不通过
public <?> void test(ArrayList<?> list) {
}
- 注意点2:不能用在泛型类的声明上
//编译不通过
class GenericClass<?> {
}
- 注意点3:不能用在创建对象上,右边属于创建集合对象,左边是声明泛型集合的引用
//编译不通过
Collection<?> collection = new ArrayList<?>();
2. 受限泛型
在 Java 中默认可以使用任何类型来实例化一个泛型类对象,也可以对泛型类实例的类型进行限制。
泛型的上限
- 格式:
类型名称 <? extends T> 对象名称
- 意义:
只能接收T类型及其子类
泛型的下限
- 格式:
类型名称 <? super T> 对象名称
- 意义:
只能接收T类型及其父类型
比如:现已知Object类,String 类,Number类,Integer类,其中Number是Integer的父类
public static void main(String[] args) {
Collection<Integer> list1 = new ArrayList<>();
Collection<String> list2 = new ArrayList<>();
Collection<Number> list3 = new ArrayList<>();
Collection<Object> list4 = new ArrayList<>();
getElement1(list1);
getElement1(list2);//报错
getElement1(list3);
getElement1(list4);//报错
getElement2(list1);//报错
getElement2(list2);//报错
getElement2(list3);
getElement2(list4);
}
// 泛型的上限:此时的泛型?,必须是Number类型或者Number类型的子类
public static void getElement1(Collection<? extends Number> coll){}
// 泛型的下限:此时的泛型?,必须是Number类型或者Number类型的父类
public static void getElement2(Collection<? super Number> coll){}
受限泛型集合数据的读取和写入
public class WildcardTest {
public static void main(String[] args) {
ArrayList<Integer> list1 = new ArrayList<>();
list1.add(5);
list1.add(12);
test1(list1);
ArrayList<Number> list2 = new ArrayList<>();
list2.add(2.5);
list2.add(1.5f);
list2.add(10);
test2(list2);
}
static void test1(ArrayList<? extends Number> list) {
list.add(null);
//list.add(10); //报错
for (Number number : list) {
System.out.println(number);
}
}
static void test2(ArrayList<? super Number> list) {
list.add(12.5);
list.add(20);
for (Object o : list) {
System.out.println(o);
}
}
}
<? extends T>
只能用来读取数据,不能向集合中存储数据,不能使用 add 方法;<? super T>
既能读取数据(需声明为 Object 类型),也能存储数据。
频繁往外读取内容的,适合用 <? extends T >
;经常往集合里插入数据的,适合用 <? super T>