泛型类型通配符

为什么需要类型通配符

现在有个需求:方法接收一个集合参数,遍历集合并把集合元素打印出来,怎么办?
按照我们没有学习泛型之前,我们可能会这样做:
public void test(List list){

for(int i=0;i<list.size();i++){
    
    System.out.println(list.get(i));

}

}

上面的代码是正确的,只不过在编译的时候会出现警告,说没有确定集合元素的类型…这样是不优雅的…
那我们学习了泛型了,现在要怎么做呢??有的人可能会这样做:
public void test(List list){

for(int i=0;i<list.size();i++){
    
    System.out.println(list.get(i));

}

}
这样做语法是没毛病的,但是这里十分值得注意的是:该test()方法只能遍历装载着Object的集合!!!
强调:泛型中的并不是像以前那样有继承关系的,也就是说List和List是毫无关系的!!!!
那现在咋办???我们是不清楚List集合装载的元素是什么类型的,List这样是行不通的…于是Java泛型提供了类型通配符 ?
所以代码应该改成这样:
public void test(List<?> list){

for(int i=0;i<list.size();i++){
    
    System.out.println(list.get(i));

}

}
?号通配符表示可以匹配任意类型,任意的Java类都可以匹配…
现在非常值得注意的是,当我们使用?号通配符的时候:就只能调对象与类型无关的方法,不能调用对象与类型有关的方法。
记住,只能调用与对象无关的方法,不能调用对象与类型有关的方法。因为直到外界使用才知道具体的类型是什么。也就是说,在上面的List集合,我是不能使用add()方法的。因为add()方法是把对象丢进集合中,而现在我是不知道对象的类型是什么。。

通配符上限

首先,我们来看一下设定通配符上限用在哪里…
现在,我想接收一个List集合,它只能操作数字类型的元素【Float、Integer、Double、Byte等数字类型都行】,怎么做???
我们学习了通配符,但是如果直接使用通配符的话,该集合就不是只能操作数字了。因此我们需要用到设定通配符上限
List<? extends Number>
上面的代码表示的是:List集合装载的元素只能是Number的子类或自身

public static void main(String[] args) {


    //List集合装载的是Integer,可以调用该方法
    List<Integer> integer = new ArrayList<>();
    test(integer);

    //List集合装载的是String,在编译时期就报错了
    List<String> strings = new ArrayList<>();
    test(strings);

}


public static void test(List<? extends Number> list) {
    
}

通配符下限

既然上面我们已经说了如何设定通配符的上限,那么设定通配符的下限也不是陌生的事了。直接来看语法吧
//传递进来的只能是Type或Type的父类
<? super Type>
设定通配符的下限这并不少见,在TreeSet集合中就有…我们来看一下
public TreeSet(Comparator<? super E> comparator) {
this(new TreeMap<>(comparator));
}
那它有什么用呢??我们来想一下,当我们想要创建一个TreeSet类型的变量的时候,并传入一个可以比较String大小的Comparator。
那么这个Comparator的选择就有很多了,它可以是Comparator,还可以是类型参数是String的父类,比如说Comparator…
这样做,就非常灵活了。也就是说,只要它能够比较字符串大小,就行了
经评论去补充:在泛型的上限和下限中有一个原则:PECS(Producer Extends Consumer Super)
书上是这样写的:
带有子类限定的可以从泛型读取【也就是—>(? extend T)】-------->Producer Extends
带有超类限定的可以从泛型写入【也就是—>(? super T)】-------->Consumer Super

通配符和泛型方法

大多时候,我们都可以使用泛型方法来代替通配符的…

//使用通配符
public static void test(List<?> list) {

}

//使用泛型方法
public <T> void  test2(List<T> t) {
    
}

上面这两个方法都是可以的…那么现在问题来了,我们使用通配符还是使用泛型方法呢??
原则:
如果参数之间的类型有依赖关系,或者返回值是与参数之间有依赖关系的。那么就使用泛型方法
如果没有依赖关系的,就使用通配符,通配符会灵活一些.

3.6泛型擦除
泛型是提供给javac编译器使用的,它用于限定集合的输入类型,让编译器在源代码级别上,即挡住向集合中插入非法数据。但编译器编译完带有泛形的java程序后,生成的class文件中将不再带有泛形信息,以此使程序运行效率不受到影响,这个过程称之为“擦除”。
3.6.1兼容性
JDK5提出了泛型这个概念,但是JDK5以前是没有泛型的。也就是泛型是需要兼容JDK5以下的集合的。
当把带有泛型特性的集合赋值给老版本的集合时候,会把泛型给擦除了。
值得注意的是:它保留的就类型参数的上限。

    List<String> list = new ArrayList<>();

    //类型被擦除了,保留的是类型的上限,String的上限就是Object
    List list1 = list;

如果我把没有类型参数的集合赋值给带有类型参数的集合赋值,这又会怎么样??

    List list = new ArrayList();
    List<String> list2 = list;

它也不会报错,仅仅是提示“未经检查的转换”

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值