对Java通配符的个人理解(以集合为例)
对Java通配符的个人理解(以集合为例)
- 前言:最近在学习Java,当学到了泛型的通配符时,不是很理解PECS(Producer Extends Consumer Super)原则,以及<? extends E> 不能使用add方法和<? super E> 不能使用get方法(注意:仅能使用Object o = list.get(0);取得是Object对象),所以我对它进行了学习和理解,这篇博文用了简单通俗的方法去讲解add和get在通配符中的使用场景以及PECS原则。可能本人的水平有限,如果我的理解有误或者内容错误,欢迎指出来,我好进行及时修改。
一、什么是泛型通配符
<一>简单定义泛型统配符
1. 在了解泛型的统配符之前,我们先了解下什么是泛型,泛型是一种包含类型参数的类,值得注意的一点是这里的类型必须是引用数据类型,而且放在尖括号< >内,这里引进了类型参数,将类直接作为了参数。
2. 那么是什么泛型统配符呢,我没有找到定义,所以我自己给它下了个定义。泛型通配符是在泛型的使用中,用来表示对泛型类型进行类型范围限定的特殊符号。这里用通配符就是为了表明要输入的类型要在一定范围之内,说的通俗一些其实就是一个类型取值范围,而最大值是Object这是确定的。
<二>泛型通配符的分类
1. <?>:无限通配符,可以在?中放入里放入任何引用数据类型,用来接收任意引用数据类型。
2. <? extends E>:这个表明的是通配符的上界,通俗讲就如同取值范围中的负无穷到E,即小于等于E的所有类型,
因为E是最大的类型(最大可以达到Object),表明可以输入所有的E的子类和E,等下会进行细致的讲解。
3. <? super E>:这个表明的是通配符的下界,通俗讲其取值范围就是E到最大值Object(因为Object是所有类的基类),就是大于等于E,小于等于Object类。
- 注意:这里能制定上界或者下界,但是不能同时制定,然后<? extends E>中的extends不一定表示类与类的继承还可以表示实现的关系,然后通配符一般是用在方法的形参声明和方法调用上,无法用于定义类和接口中。
二、泛型通配符讲解
<一>通配符的使用以及代码演示
1.无限通配符<?>的使用:可以传入任何引用数据类型
A 在调用方法时使用?通配符的过程中无法使用add方法。
原因分析:因为通配符?代表任意的数据类型,但是当我们调用的时候或者用在方法的声明上,其实这个时候还是没有给?通配符一个指定的引用数据类型,那么Java出于安全起见,就不可能允许添加元素。
B 以上的add方法虽然无法调用,add(null)是例外。
原因分析:因为null可以给任意引用数据类型赋值,代表任意引用数据,但是很容易引起NullPointerException。
C 注意使用List<?>和List<Object>当作形参时的作用不能等同,比如当传入List<Integer>时List<?>可以接收,但是List<Object>无法接收。
原因分析:因为?代表任何参数类型可以接收,但是List<Object>中虽然Object是所有子类的父类,但是List<Object>不是List<Integer>的父类,List<Object>是ArrayList<Object>等类的父类,这就是为什么泛型前后要一致的原因,从数组的角度去理解集合就比如Object[ ] arr不是Integer[ ] arr1的父类。
package com.test; import java.util.ArrayList;
import java.util.List; public class InterviewList { public static void main(String[] args) {
// ArrayList<Object> list2 = new ArrayList<Integer>();// 泛型前后要一致
ArrayList<?> list = new ArrayList<Integer>();
// list.add("122");// 注意此处无法添加"122"字符串,但是可以添加null,因为null可以给任何引用数据类型赋值
list.add(null);// 添加null没有报错
Object o = list.get(0);
System.out.println(o); ArrayList<Integer> list1 = new ArrayList<Integer>();
list1.add(34);
// Error:(18, 24) java: 不兼容的类型: java.util.ArrayList<java.lang.Integer>无法转换为java.util.List<java.lang.Object>
// 报错了,原因因为这里要接收的是List<Object>,那么可以接收的有ArrayList<Object>或者List<Object>等与List<Object>有继承关系的集合,但是无法接收ArrayList<Integer>,因为和List<Object>没有继承关系,写成List<?>才能使用
// printCollection1(list1); printCollection(list1);// ? 通配符可以正常接收
} public static void printCollection(List<?> col) {//此方法使用了无限通配符
for (Object object : col) {
System.out.println(object);
}
} public static void printCollection1(List<Object> col) {//此方法没有使用泛型通配符
for (Object object : col) {
System.out.println(object);
}
}
}
2.上界通配符<? extends E>的使用:可以传入E和E的子类
A <? extends E>作为形参时例如List<? extends E>可以使用集合的get方法来获取E或者E类型本身的元素。
原因分析:当我们用get方法时我们其实是在获取集合里内部的元素,但是我们的集合的数据类型还没有确定,但是我们可以获得一些明确的已知条件,那就是在<? extends E>中最大的类型是E,而且这个E最大是Object,所以我们可以利用这一点,那么我们就可以清楚地了解到该集合里面的获取的元素肯定是E或者Object的子类,他们的范围肯定小于E或者Object,那么我们就可以用Object和E这两个范围比集合里面的元素大的类去接收集合里面的元素。(注:可能略显啰嗦但是我就是想解释清楚。)
B 在使用上界通配符时,无法调用add方法来添加非null的元素。
原因分析:由于上面已经说得很清楚了,<? extends E>作为形参时例如List<? extends E>这时最大类型是E和Object,但是我们不清楚最小的类型是什么,因为此时?这个通配符没有被赋值,我们调用add方法是要添加集合元素或者集合元素的子类,但是我们没法明确肯定该集合元素类型,或者比该集合元素范围更小的子类,那么Java就不会允许添加元素。
public class WildCardTest2 { public static void main(String[] args) {
ArrayList<? extends Number> list = new ArrayList<>();
// list.add(3);报错了,无法添加非null元素
list.add(null);//没有报错
Object o = list.get(0);//用Object接收没有报错
Number n = list.get(0);//用Number接收没有报错
} }
3.下界通配符<? super E>的使用:可以传入E或者E的父类
A 在使用下界通配符时,无法使用get方法获取Object以外的元素,或者需要向下转型,但是可能出现ClassCastException的异常。
原因分析:上界通配符,在使用get方法的时候,此时类型没有明确还是问号?我们只能明确其最大父类或者接口时,我们才能接收,但是我们只能明白<? super E>作为形参时例如List<? super E>时,只能明确Object是最大父类,其他的一概不知,所以只能Object o = list.get(0)。
B 可以使用集合的add方法添加E或者E的子类。
原因分析:上界通配符已经解释很清楚了,add方法添加元素时,?类型不确定就要明确该?类型的最小子类,只要比可能存在的最小子类或者子接口小的任意引用数据类型的对象,我们都可以将其添加,而下界通配符<? super E>当作形参时例如List<? super E>,此时E就是最小子类,此时add方法可以添加E或者E的子类。
public class WildCardTest3 { public static void main(String[] args) {
ArrayList<? super Number> list = new ArrayList<>();
list.add(3);//没有报错,自动装箱成Integer,Number的子类
list.add(3.4F);//没有报错,自动装箱成Float,Number的子类
list.add(32L);//没有报错,自动装箱成Long,Number的子类
Object o = list.get(1);
// Integer i = list.get(0);//报错了,无法用Integer接收
} }
<二>对PECS原则的解读
1.什么是PECS原则?
PECS是Producer Extends Consumer Super的递归缩写,是Java中使用泛型通配符的原则。
2.阿里巴巴的通配符使用规约
泛型通配符<? extends T>来接收返回的数据,此写法的泛型集合不能使用 add 方法,而 < ? super T> 不能使用 get 方法,做为接口调用赋值时易出错。
说明:扩展说一下 PECS<Producer Extends Consumer Super> 原则:第一、频繁往外读取内容的,适合用<? extends E>。第二、经常往里插入的,适合用<? super E> 。
3.对PECS原则的简单解读
字面意思是生产者要被继承要被当作上界通配符<? extends E>的上界E,消费者要继承其他类要被当成下界通配符<? super E>的下界E,再借助下阿里巴巴的泛型开发规约去理解下,应该就是当这个被传入的类型需要进行很多get操作获取数据的话,那么请使用上界通配符这时这个上界就如同生产者一样,因为它能被不断get到,而当需要不断进行add方法添加数据的话,请使用下界通配符这时这个下界就如同消费者一样,因为它不断地索取,因为我们在不断地add元素给它。
对Java通配符的个人理解(以集合为例)的更多相关文章
- java基础笔记(9)----集合之list集合
集合 对于集合的理解,集合是一个容器,用于存储和管理其它对象的对象 集合,首先了解所有集合的父接口----collection 特点:存储任意object元素 方法 boolean add(Objec ...
- Java基础学习笔记十五 集合、迭代器、泛型
Collection 集合,集合是java中提供的一种容器,可以用来存储多个数据. 在前面的学习中,我们知道数据多了,可以使用数组存放或者使用ArrayList集合进行存放数据.那么,集合和数组既然都 ...
- Java常用排序算法及性能测试集合
测试报告: Array length: 20000 bubbleSort : 573 ms bubbleSortAdvanced : 596 ms bubbleSortAdvanced2 : 583 ...
- Java中hashcode的理解
Java中hashcode的理解 原文链接http://blog.csdn.net/chinayuan/article/details/3345559 怎样理解hashCode的作用: 以 java. ...
- 深入Java源码剖析之Set集合
Java的集合类由Collection接口和Map接口派生,其中: List代表有序集合,元素有序且可重复 Set代表无序集合,元素无序且不可重复 Map集合存储键值对 那么本篇文章将从源码角度讨论一 ...
- Java创建List、Map等集合对象的同时进行赋值操作
title: Java创建List.Map等集合对象的同时进行赋值操作 date: 2019-11-28 23:25:47 tags: JavaSE categories: JavaSE 问题简介 ...
- Java语言的个人理解
Java语言的个人理解(比价深层次吧) 大四的生活确实十分的奢靡,不锻炼,不读书,几乎就是当一天和尚撞一天钟的生活,太颓废了,还好自己不是这个样子,不过身体确实差了很多,昨天跑了一圈内环(4KM),今 ...
- Java常量字符串String理解
Java常量字符串String理解 以前关于String的理解仅限于三点:1.String 是final类,不可继承2.String 类比较字符串相等时时不能用“ == ”,只能用 "eq ...
- java的final变量理解
java的final变量理解 final基本类型是数值不能改变 final对象是引用不能改变,对象其自身是可以被修改的.
随机推荐
- 【LOJ】#2075. 「JSOI2016」位运算
题解 压的状态是一个二进制位,我们规定1到n的数字互不相同是从小到大,二进制位记录的是每一位和后一个数是否相等,第n位记录第n个数和原串是否相等,处理出50个转移矩阵然后相乘,再快速幂即可 代码 #i ...
- 035 spark与hive的集成
一:介绍 1.在spark编译时支持hive 2.默认的db 当Spark在编译的时候给定了hive的支持参数,但是没有配置和hive的集成,此时默认使用hive自带的元数据管理:Derby数据库. ...
- IO知识点整理(文件File类的使用)
一: 1.API 2.构造函数的程序 注意这集中构造函数的特点. 同时,字段separator的使用. import java.io.File; public class Test101 { publ ...
- PHP获取访问者公网IP
if(!empty($_SERVER["HTTP_CLIENT_IP"])){ $cip = $_SERVER["HTTP_CLIENT_IP"]; } el ...
- PocketMoney
PocketMoney( Money.pas/cpp/c)Description学校为了表彰tsoi的优异成绩, m个领导每人都决定给tsoi的一些人发一些小红包.于是n个Tsoier排成一排,等待着 ...
- JAVA 传递
其实java里面都是传值,只不过基本数据类型传的是数值,而引用类型传的是对象的地址. 作者:Intopass链接:https://www.zhihu.com/question/31203609/ans ...
- 决策树ID3算法--python实现
参考: 统计学习方法>第五章决策树] http://pan.baidu.com/s/1hrTscza 决策树的python实现 有完整程序 决策树(ID3.C4.5.CART ...
- 【bzoj3451】Tyvj1953 Normal 期望+树的点分治+FFT
题目描述 给你一棵 $n$ 个点的树,对这棵树进行随机点分治,每次随机一个点作为分治中心.定义消耗时间为每层分治的子树大小之和,求消耗时间的期望. 输入 第一行一个整数n,表示树的大小接下来n-1行每 ...
- NOI.AC NOIP模拟赛 第四场 补记
NOI.AC NOIP模拟赛 第四场 补记 子图 题目大意: 一张\(n(n\le5\times10^5)\)个点,\(m(m\le5\times10^5)\)条边的无向图.删去第\(i\)条边需要\ ...
- 关于操作Access数据库jdk选择问题
关于操作Access数据库,使用jdk64位无法通过ODBC无法获取数据,只能通过jdk32位进行开发.