Java泛型通配符和泛型的上限、下限

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>

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值