java contains 通配符_Java泛型食用笔记(四) -- 通配符

Java泛型食用笔记(四) -- 通配符

1. 三种通配符

通配符为一个泛型类所指定的类型集合提供了一个有用的类型范围,Java 里有三种通配符:

无限定通配符, >

上界限定符, extends Number>

下界限定符, super Number>

上界限定符接受 extends 后面类的本身与其子类, 下界限定符接受 super 后面类的本身与其父类。无限定通配符接受任何类。

2. 无限定通配符

无限定通配符表示匹配任意类。ArrayList> 和 ArrayList,ArrayList 看上去功能有点类似,但实际却不一样。 ArrayList> 是任意 ArrayList 的超类,而我们知道 ArrayList 并不是。ArrayList> 虽然可以匹配任何类,我们并不知道那个类的类型,但我们知道里面的所有元素都有相同的类。而原始类 ArrayList 可以添加任意不同类型的元素,编译器并不能进行类型判断,但运行的时候可能会抛出异常。ArrayList 明确的告诉我们可以添加任何类型的对象。

看一个无限定通配符例子

public class GenericTest07 {

public static void printCollection(Collection col1) {

for (Object obj : col1) {

System.out.println(obj);

}

}

public static void main(String[] args) {

List list = new ArrayList<>();

list.add(1);

list.add(2);

printCollection(list); // compile error

}

}

由于 ArrayList 不是 Collection 的子类,故编译不能通过。改用无限定通配符即可编译通过:

public class GenericTest07 {

public static void printCollection(Collection> col1) {

for (Object obj : col1) {

System.out.println(obj);

}

}

public static void main(String[] args) {

List list = new ArrayList<>();

list.add(1);

list.add(2);

printCollection(list);

}

}

Collection> 是任意 T ArrayList 的超类,可以编译通过。但是,在printCollection 方法中,却不能使用任何带有类型参数的方法,比如 add(T t), 而 remove(Object obj) contains(Object obj) 等方法都是可以调用的。

再看一个例子:

public class GenericTest08 {

public static void reBox(Box> box) {

System.out.println(box.put(box.get()));

}

}

public interface Box {

public T get();

public void put(T t);

}

这段代码初看应该是 work 的,然而事实可能要让你失望了,编译会提示类型不兼容的错误。事实上,你根本就不能在此处调用 box.put() 方法,因为box.put() 的形参类型是未知的,编译器不能检验你的实参类型是否与形参类型一致。

有没有办法实现这个逻辑的,我们需要借助一个辅助方法。

public class GenericTest08 {

public static void reBox(Box> box) {

reboxHelper(box);

}

private static void reboxHelper(Box box) {

box.put(box.get());

}

}

reboxHelper 方法帮助编译器保留一部分类型信息。

上界限定符

我们知道,和数组不一样,泛型并不是协变的。比如 Dog 是 Animal 的子类,那 Dog[] 也是 Animal[] 的子类,但 List 并不是 List 的子类。这个时候上界限定符的作用就体现出来了,写法是 List extends Animal>,可以解释为“可以放入任何 Animal 及其子类的列表”,之前讲到泛型编译后会抹除类型参数成 Object 类型,当你使用上界限定符后,就抹除成上界。

看代码

public class GenericTest {

public static void main(String[] args) {

ArrayList holder = new ArrayList();

holder.add(new Integer(1));

// ArrayList numHolder = holder; // 1. compile failed. ArrayList 不是 ArrayList 的父类

ArrayList extends Number> numHolder = holder; // 2. ok

Number num = numHolder.get(0); // 3. ok. return Number

numHolder.contains(new Integer(2)); // 4. ok

// numHolder.add(new Integer(2)); // 5. compile error

}

}

1 处编译错误是因为泛型不是协变;Integer 是 Number 子类,故 2 处正确;3 处正确,正如上段所说,上界限定符抹除成上届,返回类型为 Number;4 处 ok,是因为 contains 方法接受的是 Object 类。5 处需要稍微注意,add 的形参类型也是 extends Number>,编译器只能知道类型是 Number 的子类,并不能确定具体类型是什么,因此无法验证类型的安全性。

下界限定符

下界限定符也称为超类通配符,写法是 List super Dog>, 列表可以接受 Dog 类型及其父类对象。

上一接的第 5 处,如果向往里添加元素,需要使用下界限定符。

public void writeTo(List super Integer> list) {

list.add(new Integer(2)); // ok

}

super Integer> 说明类型参数一定是 Integer 的父类。因此向里添加 Integer 类或其子类的对象一定是安全的。

PECS 原则

根据上面的例子,我们看到,使用上界限定符定义的类,可以向外提供东西,也就是说作为 Producer。使用下界限定符定义的类,可以作为 Consumer 接收外部往自身添加东西。

总结起来就是, "Producer Extends, Consumer Super":

"Producer Extends" - 如果你需要一个只读类型,用它来produce T,那么使用 extends T>

"Consumer Super" - 如果你需要一个只写类型,用它来consume T,那么使用 super T>

如果需要同时读取以及写入,那么我们就不能使用通配符了

下面一个方法同时涉及了这两条规则。

public class Collections {

public static void copy(List super T> dest, List extends T> src) {

for (int i=0; i

dest.set(i, src.get(i));

}

}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值