泛型通配符的使用场景

泛型通配符的使用场景

Java泛型支持通配符,可以单独使用一个“?”表示任意类,也可以使用extends关键字表示某一个类(接口)的子类型,还可以使用super关键字表示某一个类(接口)的父类型,但问题是什么时候该用extends,什么时候该用super呢?

1、泛型结构只参与“读”操作则限定上界(extends关键字)

阅读如下代码,想想看我们的业务逻辑操作是否还能继续:

    public static <E> void read(List<? super E> list) {
        for (Object obj : list) {
            // 业务处理
        }
    }

从List列表中读取元素的操作(比如一个数字列表中的求和计算),你觉得方法read能继续写下去吗?

​ 答案是:不能,我们不知道list到底存放的是什么元素,智能推断出是E类型的父类(当然也有可能是E类型),但问题是E类型的父类是什么呢?无法再推断,只有运行时才知道,那么编码期就完全无法操作了。当然,把你可以把它当作是Object类来处理,需要时再转换成E类型——这完全违背了泛型的初衷。

​ 在这种情况下,“读”操作如果期望从List集合中读取数据就需要使用extends关键字了,也就是要界定泛型的上界,代码如下:

    public static <E> void read(List<? extends E> list) {
        for (E e : list) {
            // 业务处理

        }
    }

​ 此时,已经推断出List集合中取出的是E类型的元素。具体是什么类型的元素就要等到运行时才能确定了,但它一定是一个确定的类型,比如read(Arrays.asList(“A”))调用该方法时,可以推断出List中的元素类型是String,之后就可以对List中的元素进行操作了,如加入到另外的List集合中,或者作为Map<E,V>的键等。

2、泛型结构只参与“写”操作则限定下界(使用super关键字)

先看如下代码是否可以编译:

    public static void write(List<? extends Number> list) {
        // 加入一个元素
        list.add(123);
    }

​ 编译失败,失败的原因是list中的元素类型不确定,也就是编译器无法判断出泛型类型到底是什么,是Integer类型?是Double?还是Byte?这些都符合extends关键字的定义,由于无法确定时机的泛型类型,所以编译器拒绝了此类操作。

​ 在这种情况下,只有一个元素可以add进去:null值,这是因为null是一个万用类型,它可以使所有类的实例对象,所以可以加入到任何列表中。

​ Object是否可以?不可以,因为它不是Number子类,而且即使把list变量修改为List<? extends Object>类型也不能加入,原因很简单,编译器无法推断出泛型类型,加什么元素都是无效的。

​ 在这种“写”操作的情况下,使用super关键字限定泛型类型的下界才是王道,代码如下:

    public static void write(List<? super Number> list) {
        list.add(123);
        list.add(3.14);
    }

甭管它是Integer类型的123还是Float类型的3.14,都可以加入到list列表中,因为它们都是Number类型,这就保证了泛型类的可靠性。

​ 对于要限定上界还是限定下界,JDK的Collections.copy方法是一个非常好的的例子,它实现了把源列表中的所有元素拷贝到目标列表中对应的索引位置上,代码如下:

public static <T> void copy(List<? super T> dest, List<? extends T> src) {
    for (int i=0; i<srcSize; i++)
       dest.set(i, src.get(i));
}

源列表是用来提供数据的,所以src变量需要限定上界,带有extends关键字,目标列表是用来写入数据的,所以dest变量需要界定上界,带有super关键字。

​ 如果一个泛型结构即用作“读操作”又用作“写操作”,那该如何进行限定呢?不限定,使用确定的泛型类型即可,如List。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1. `HashTable` 是 Java 中的一个哈希表实现,它继承自 `Dictionary` 类,实现了 `Map` 接口。`HashTable` 使用键值对的方式存储数据,其中键和值都是对象类。 特点: - 线程安全:`HashTable` 是同步的,多线程环境下可以安全使用。 - 键值不允许为 null:`HashTable` 不允许使用 null 作为键或值,否则会抛出 NullPointerException。 - 哈希冲突解决:使用链表法解决哈希冲突,即在哈希表的每个位置上维护一个链表,当多个键映射到同一个位置时,将它们链接在一起。 2. `Collections` 是 Java 中提供的工具类,提供了一系列静态方法,用于操作集合(`Collection`)和地图(`Map`)。其中一些常用方法包括: - `sort()`:对集合进行排序。 - `binarySearch()`:在有序集合中执行二分查找。 - `reverse()`:反转集合中的元素顺序。 - `shuffle()`:随机打乱集合中的元素顺序。 - `max()`、`min()`:返回集合中最大或最小的元素。 - `addAll()`:将多个元素添加到集合中。 - `frequency()`:计算指定元素在集合中出现的次数。 3. 是 Java 的一个特性,它允许在编译时指定集合中存储的元素类。通过使用,可以在编译时检查类安全性,并减少在运行时出现类转换错误的可能性。 在集合中使用的作用: - 提供类安全性:可以防止将错误类的对象放入集合中。 - 简化代码:避免了手动进行类转换,使代码更加清晰和简洁。 - 提高性能:避免了运行时的类检查和类转换。 4. `? extends T` 是通配符中的一种形式,表示可以接受 T 类及其子类的参数。这种通配符限制了具体的类范围,可以用于声明方法参数、变量或返回值。 例如,`List<? extends Number>` 表示一个存储 Number 或其子类的列表,可以接受 Integer、Double 等具体类的列表作为参数。 在使用 `? extends T` 通配符时,只能读取集合中的元素,不能添加新的元素到集合中。因为编译器无法确定具体的类,只能确保从集合中读取的元素是 T 类或其子类。 5. `HashSet` 和 `HashMap` 都是 Java 中的集合类。 `HashSet` 是基于哈希表实现的无序集合,它使用哈希函数来计算元素的存储位置,具有快速的插入、删除和查找操作。`HashSet` 不允许重复元素,当尝试向 `HashSet` 中插入重复元素时,插入操作会被忽略。 `HashMap` 是基于哈希表实现的键值对存储结构。它也使用哈希函数来计算键的存储位置,可以通过键来快速查找对应的值。`HashMap` 允许键和值都为 null,并且允许重复的值。 在底层实现上,`HashSet` 实际上是通过一个 `HashMap` 来实现的,`HashSet` 的元素被存储为 `HashMap` 的键,而值则是一个常量对象。 6. `BigDecimal` 是 Java 中用于精确表示和计算大数字的类,提供了高精度的十进制计算。 `BigDecimal` 适用于需要高精度计算的场景,例如金融计算、货币计算等,可以避免使用浮点数导致的精度损失问题。 `BigDecimal` 的算术运算方法包括加法、减法、乘法、除法等,这些方法都是精确计算的,并且可以指定舍入模式来控制结果的精度和舍入方式。 例如,使用 `BigDecimal` 进行加法运算可以通过 `BigDecimal.add()` 方法实现: ```java BigDecimal num1 = new BigDecimal("10.25"); BigDecimal num2 = new BigDecimal("5.75"); BigDecimal sum = num1.add(num2); System.out.println(sum); // 输出: 16.00 ``` 在进行算术运算时,需要使用 `BigDecimal` 的方法进行操作,而不是直接使用运算符。这样可以确保精确的计算结果,并且可以灵活地控制舍入方式和精度。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值