Java集合与泛型中的陷阱
List,List<Object>区别
List<Integer> t1 = new ArrayList<>();
// 编译通过
List t2 = t1;
//编译失败
List<Object> t3 = t1;
t1 可以赋给 t2, 但是 t1 不能赋给 t3,会抛出如下异常
Error:(16, 35) java: 不兼容的类型: java.util.List<java.lang.Integer>无法转换为java.util.List<java.lang.Object>
List<?>注意点
List<Object> t1 = new ArrayList<>();
List<?> t2 = t1;
// 编译通过
t2.remove(0);
t2.clear();
// 编译不通过
t2.add(new Object());
List<?> 是一个泛型,在没有赋值之前,是可以接受任何集合的赋值的,但是请注意,赋值之后就不能往里面添加元素了
提示如下错误:
Error:(18, 19) java: 对于add(java.lang.Object), 找不到合适的方法
方法 java.util.Collection.add(capture#1, 共 ?)不适用
(参数不匹配; java.lang.Object无法转换为capture#1, 共 ?)
方法 java.util.List.add(capture#1, 共 ?)不适用
(参数不匹配; java.lang.Object无法转换为capture#1, 共 ?)
所以 List<?> 一般用来作为参数来接受外部的集合,或者返回一个不知道具体元素的集合。
<? extends T> 与 <? super T>
<? extends T>
<? extends T> a,a 这个变量可以接受 T 及其 T 子类的集合,上界为 T,并且从 a 取出来的类型都会被强制转换为 T
class Animal{
}
class Cat extends Animal{
}
class RedCat extends Cat{
}
demo:
List<Animal> animals = new ArrayList<>();
List<Cat> cats = new ArrayList<>();
List<RedCat> redCats = new ArrayList<>();
// 可以通过编译
List<? extends Cat> extendsCat = redCats;
// 不能通过编译,因为只能接受 Cat 及其子类的集合
extendsCat = animals;
// 重点注意:下面三行都不能通过编译
extendsCat.add(new Animal());
extendsCat.add(new Cat());
extendsCat.add(new RedCat());
// 重点注意:可以通过编译
extendsCat.add(null);
<? extends T>最需要注意的是,就是不能向里面添加除null之外的其他所有元素,这个和 List<?> 有点类似
<? super T>
<? super T>,它和 <? extends T> 有点相反。**对于 <? super T> a**,a 这个变量可以接受 T 及其 T 父类的集合,下界为 T,并且从 a 取出来的类型都会被强制转换为 Object
demo:
List<Animal> animals = new ArrayList<>();
List<Cat> cats = new ArrayList<>();
List<RedCat> redCats = new ArrayList<>();
// 可以通过编译
List<? super Cat> superCat = animals;
// 不能通过编译,因为只能接受 Cat 及其父类的集合
superCat = redCats;
// 重点注意:不能通过编译,只能添加 Cat 及其 Cat 的子类
superCat.add(new Animal());
// 重点注意,可以通过编译
superCat.add(new Cat());
superCat.add(new RedCat());
superCat.add(null);
注意,<? super T>最需要注意的是,在虽然可以接受 T 及其父类的赋值,但是只能向里面添加 T 及其 T 的子类。
总结
1、List<? extends T> a ,可以把 a 及其 a 的子类赋给 a,从 a 里取的元素都会被强制转换为 T 类型,不过需要注意的是,不能向 a 添加任何除 null 外是元素。
2、List<? super T> a ,可以把 a 及其 a 的父类赋给 a,从 a 里取的元素都会被强制转换为 Object 类型,不过需要注意的是,可以向 a 添加元素,但添加的只能是 T 及其子类元素。
List泛型与重载
你觉得下面这道题能够编译通过吗?
class GernerTypes {
public static void method(List<Integer> list) {
System.out.println("List<Integer> list");
}
public static void method(List<String> list) {
System.out.println("List<String> list");
}
}
不通过编译,错误信息:
Error:(20, 28) java: 名称冲突: method(java.util.List<java.lang.String>)和method(java.util.List<java.lang.Integer>)具有相同疑符
两个方法的参数不同,为什么会重载不通过呢?
实际上在 Java 的泛型中,泛型只存在于源码中,在编译后的字节码中,泛型已经被替换为原生类型了,并且在相应的地方插入了强制转换的代码。
所以上面的两个方法,看似参数不一样,但是经过编译擦出之后,他们的参数就是一样的了,所以编译不通过。
数组集合转换:
String[] arr = {"one", "two", "three"};
// 数组转换成集合
List<String> list = Arrays.asList(arr);
// 向集合添加元素:编译正常,但运行时抛出了异常
list.add("four");
运行时报错:
Exception in thread "main" java.lang.UnsupportedOperationException
at java.util.AbstractList.add(AbstractList.java:148)
at java.util.AbstractList.add(AbstractList.java:108)
at com.qhong.basic.list.test.main(test.java:16)
问题来了,向集合添加元素为啥会抛出异常呢??
我们先来看一下 Arrays.asList(arr) 方法究竟返回了什么?
看源码:
public static <T> List<T> asList(T... a) {
return new ArrayList<>(a);
}
/**
* @serial include
*/
private static class ArrayList<E> extends AbstractList<E>
implements RandomAccess, java.io.Serializable
{
private static final long serialVersionUID = -2764017481108945198L;
private final E[] a;
ArrayList(E[] array) {
a = Objects.requireNonNull(array);
}
。。。。
返回的List内部直接引用了原数组arr
原数组长度固定为3,所以不可以再add
下面的代码证明这一点
String[] arr = {"one", "two", "three"};
// 数组转换成集合
List<String> list = Arrays.asList(arr);
// 修改 arr
arr[0] = "0";
//打印看看
System.out.println(list.get(0));
打印:
0
建议大家这样转换比较安全
List<String> list = new ArrayList<>(Arrays.asList(arr));
参考:
Java集合与泛型中的陷阱的更多相关文章
- Java集合与泛型中的几个陷阱,你掉进了几个?
下面我总结了集合.泛型.数组转集合等一些常见的陷进,认真看完,相信你绝对有所收获. 1.List ,List<?> 与 List<Object> 有区别吗? 说实话,我敢保证很 ...
- Java集合之泛型的使用
Java集合之泛型的使用 泛型提供了一种轻便灵活的数据操作,数据的安全性相对提高. 泛型提供了对列表元素的约束条件,比如ArrayList有序链表,可存储任意类型的元素. 此处构建一个ArrayLis ...
- 浅入深出之Java集合框架(中)
Java中的集合框架(中) 由于Java中的集合框架的内容比较多,在这里分为三个部分介绍Java的集合框架,内容是从浅到深,如果已经有java基础的小伙伴可以直接跳到<浅入深出之Java集合框架 ...
- Java进阶4表达式中的陷阱
Java进阶4表达式中的陷阱 20131103 表达式是Java中最基本的组成单元,各种表达式是Java程序员最司空见惯的内容,Java中的表达式并不是十分的复杂,但是也有一些陷阱.例如当程序中使用算 ...
- Java集合和泛型
集合 常用的集合有ArrayList,TreeSet,HashMap,HashSet. ArrayList 最常用的集合,每次插入都在后面追加元素. TreeSet 以有序状态保持并可防止重复.当你需 ...
- Java 集合和泛型
一.集合(Collections) Java使用集合来组织和管理对象. 1.Java的集合类 集合类主要负责保存.盛装和管理对象,因此集合类也被称为容器类. 集合类分为Set.List.Map和Que ...
- java——集合、泛型、ArrayList、LinkedList、foreach循环、模拟ktv点歌系统
集合:一系列特殊的类,这些类可以存储任意类型的对象,长度可变,集合类都在java.util包中. 但是集合记不住对象的类型,当把对象从集合中取出时这个对象的编译类型就变成了Object类型.这样在取元 ...
- Java 集合框架_中
Set接口 特点: [1]Set接口表示一个唯一.无序的容器(和添加顺序无关) Set接口常用实现类有 HashSet [1]HashSet是Set接口的实现类,底层数据结构是哈希表. [2]Hash ...
- java集合示例 小心重载的陷阱
package com.hra.riskprice; import com.hra.riskprice.SysEnum.Factor_Type; import org.springframework. ...
随机推荐
- Python 3+selenium+unittest+HTMLTestRunner生成测试报告
一.下载HTMLTestRunner.py,解压,将它放到 python安装路径的site-packages目录下 https://pan.baidu.com/s/1epWlibxbxWlNoIcxL ...
- ambari卸载集群
#1.删除hdp.repo和hdp-util.repo cd /etc/yum.repos.d/ rm -rf hdp* rm -rf HDP* #rm -rf ambari* #2.删除安装包 #用 ...
- Get WMS Static GoodLocation By Dynamic SQL
Dynamic SQL Store Procedure: Note: use variable,you need convert varchar and as a variable,not direc ...
- Vue.js中使用select选择下拉框
在Vue.js中使用select选择下拉框有两种方法: 第一种: Add.html: <select v-model="sysNotice.noticeType" id=&q ...
- Windows下配置node和npm
Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境.Node.js 使用了一个事件驱动.非阻塞式 I/O 的模型,使其轻量又高效. Node.js 的使用包管理器 ...
- MySQL中0、'0'作为条件时的区别
一.现象: 今天查询时,某字段x为varchar 可能的值为A B C 0 1 2 3 4 想把0 1 2 3 4的所有数据查出,使用的SQL为 select * from table where x ...
- FreeSwitch 终端命令详细介绍
FreeSwitch版本:1.6.9 以下为部分终端命令 alias 语法: alias [add|stickyadd] <alias> <command> | del [&l ...
- Extjs6 grid 导出excel功能类,支持renderer
/* grid 导出excel扩展(纯客户端,提交到后台再导的可以自己改改代码也在) 参考自 https://blog.csdn.net/tianxiaode/article/details/4596 ...
- android模拟器访问PC本地接口
一般来讲PC本地接口是localhost:8080 而在安卓模拟器上用的话,他会映射模拟器本身的,也就是说,可以把模拟器也当成一个PC端来看待,这样会好理解点吧 而在模拟器上想要访问PC本地的loca ...
- 设计 mysql的单例模式及完整功能
class MySQLDB{ private $host; private $port; private $username; private $password; private $charset; ...