Java—泛型中的通配符

通配符类型

通配符有 3 种形式

  • < ? > 被称作无限定的通配符
  • < ? extends T> 被称作有上限的通配符。
  • < ? super T> 被称作有下限的通配符。
1>无限定通配符
public void testWildCards(Collection<?> collection){
}

上面的代码中,方法内的参数是被无限定通配符修饰的 Collection 对象,它隐略地表达了一个意图或者可以说是限定,那就是 testWidlCards() 这个方法内部无需关注 Collection 中的真实类型,因为它是未知的。所以,你只能调用 Collection 中与类型无关的方法(即不能向collection中添加新元素)


2> < ? extends T> 和 < ? super T>
  • 很多人写博客的时候都把这两点分开写,并且对其中的一些蕴含的原理含糊其辞一笔带过,其实这里又涉及到了我们的继承和多态的知识,下面看这个例子:
    首先,我们构建四个类
class GrandF{

}
class Father extends GrandF{

}
class  Son extends Father{

}
class GrandS extends Son{

}

下面是这四个类之间的继承关系

image.png

当我们执行这段代码时:

class TestSub {
    public void testSub(Collection<? extends GrandF> para){
        para.add(new GrandF());             //Error    
        para.add(new Father());             //Error    
        para.add(new Son());                //Error    
        para.add(new GrandS());             //Error    
    }
}
  • 以上四句代码编译器都会报错。很多人会疑惑:明明声明了集合内元素的类型必须是GrandF或它的的子类,那我为什么就不能add进去呢?
    这里我们看上面的继承关系图,其实就是一句话:子类是父类类型的,但父类不是子类类型的

< ? extends GrandF> 其实就是对集合内元素类型的约束,没错,它是要求这个集合内元素类型必须在这个范围内,但是一旦它以参数形式传入时,就确定了它的类型,但在这段代码块中,编译器只能确定,传入的集合的元素类型是GrandF或它的的子类,但当元素是GrandF的子类时,父类GrandF并不属于子类类型,自然不能add进去(类型不匹配)。同理,Father类,Son类,GrandS类都不能add进去,因为你只知道上界在GrandF处,但无法确定下界到底在哪里,所以无法向其中添加新的元素

  • 很多博客在这里只说明了在使用?通配符时无法进行写操作,我觉得这样解释并不全面,也没有道出其中的原理

但当我们执行代码:

class TestSub {
    public void testSub(Collection<? super GrandF> para){
        para.add(new GrandF());
        para.add(new Father());
        para.add(new Son());
        para.add(new GrandS());
    }
}

编译器居然没有报错!很多小伙伴都惊呆了,但如果看到这里也感到惊讶的话就证明你没有理解上面的那一段话,我们再来解释一遍:

在这段代码中,要求集合中元素的类型必须是GrandF或其父类,但是无论这个集合中元素类型是谁,它肯定是 GrandF,Father,Son,GrandS的父类,没错,又是那句话:

  • 子类是父类的类型,但父类不是子类的类型

在这里 我们指定了下界是GrandF 但是不知道上界到底在哪里。所以这个时候,我们所看到的这四个类,都属于GrandF或其父类的类型,所以都可以add进去

上面的这些东西确实有些绕,理解起来也有些困难,但这确实是多态与泛型结合的一个典型问题,一定要理解,可以自己敲几遍代码测试。

  • 最后总结一下,? extends 是上界通配符(已知上界但不知道下界) ? super 是下界通配符(知道下界但不知道上界)。只有当我们能确定下界时,我们才能确定我们add进去的元素类型都是属于 这个下界或这个下界的父类的类型(多态)。其实就是一句话,在有通配符的地方,只能对下界及下界以下的类的对象进行写的操作
### Java 及其通配符的用法 #### 简介 Java 编程语言自 JDK 1.5 起引入了这一特性,允许定义类安全的方法、类和接口而无需指定具体的类参数[^1]。 #### 方法示例 通过使用方法可以在不牺牲类安全性的情况下提高灵活性。当希望某个方法能够处理多种数据类的对象时,可以声明该方法为方法: ```java public class Util { // 定义一个简单的交换函数作为方法的例子 public static <T> void swap(T[] array, int i, int j) { T temp = array[i]; array[i] = array[j]; array[j] = temp; } } ``` 上述代码展示了如何创建接受任意数组并能对其元素进行互换操作的通用工具类 `Util` 中的一个静态成员函数 `swap()` 方法[^3]。 #### 使用通配符增强灵活性 为了进一步提升程序设计中的弹性,在某些场景下可能需要利用通配符来代替具体类参数。例如,如果想要编写既能读取又能写入集合内项目的算法,则应采用上下界限定的方式实现: - **上界通配符** (`<? extends A>`): 表明未知的具体类是给定类的子类; - **下界通配符** (`<? super B>`): 则意味着未知的确切类至少实现了特定父类的功能; 下面是一个展示如何运用这两种形式的操作实例: ```java import java.util.ArrayList; import java.util.List; // 假设有两个接口A,B以及它们各自的实现类C,D,E,F... interface A {} class C implements A {} interface B {} class D implements B {} class E extends Object implements B {} // 下面这个例子说明了如何使用带有限制条件的通配符 void copy(List<? extends Number> sourceList, List<? super Integer> destList){ for (Number element : sourceList) { // 只有当sourceList里的所有元素都可以被转换成Integer的时候才能放入destList中去 if(element instanceof Integer){ destList.add((Integer)element); }else{ System.out.println("Element cannot be cast to Integer"); } } } ArrayList<Integer> integerNumbers = new ArrayList<>(); integerNumbers.add(1);integerNumbers.add(2); ArrayList<Number> numbers = new ArrayList<>(integerNumbers); ArrayList<Object> objects = new ArrayList<>(); copy(numbers,objects); // 正确调用 ``` 此段代码片段解释了如何借助于带有边界约束的通配符完成不同类列表间的安全复制过程[^2]。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值