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. ...
随机推荐
- Google 内部代码是不支持异常(Excepton)的,C++ 异常的优劣之处有许多讨论(知乎上的讨论)
最近 Google 开源了其内部多年使用的 C++ 代码库 Abseil 作为 C++ 标准库的补充,并会对其进行持续更新,本文对其进行简要介绍. 一句话概括,这个库的特点是用 C++ 11 的代码实 ...
- pandas(三)
合并数据集: 创建一个能创建dataframe的函数 def make_data(cols,ind): data={c:[strc(c)+str(i) for i in ind] for c in c ...
- jeecg自定义按钮使用exp属性不起作用
为什么要写这篇文章? 之前写过一篇类似的文章 jeecg笔记之自定义显示按钮exp属性,但是有些小伙伴留言参考后不起作用,当时我的 jeecg 版本为3.7.5,最终以版本不同,暂时搁浅了.今天,重新 ...
- webservice学习教程(三)--
快速入门 首先,我们来尝试一下调用别人写好的webService 来体验一把:我们访问http://www.webxml.com.cn/zh_cn/index.aspx 进入到里边 当我们输入一个号码 ...
- UISplitViewController使用
分割控制器UISplitViewController <1>功能:它也是ipad的一个新特性,在屏幕上可以同时显示两个控制器,左边一个,右边一个:左边的为主控制器,右边的为详细控制器,主控 ...
- ADB——keyevent命令
基本格式 adb shell input keyevent xxx # xxx为具体操作对应的数字 keycode 官方 KEYCODE 链接:戳这里 0 KEYCODE_UNKNOWN 未知按键 1 ...
- jenkin如何实现web版本控制&回退
jenkins本身作为一款运维利器,具备 1. 持续集成 (Continuous integration) 2. 持续交付(Continuous delivery) 3. 持续部署(continuou ...
- 使用dbms_profiler收集存储过程每步执行时间
最近和优化团队的专家学到一个很有意义的内置包:dbms_profiler,专门用于分析Oracle存储过程中的各段代码的时间开销情况,从而快速找到性能瓶颈的步骤. 1.sys创建dbms_profil ...
- 解决postman环境切换,自动获取api签名时间及签名
postman调试api接口时,常遇到两个问题: 1.环境分为开发环境,测试环境,正式环境,如何只写一个接口,通过切换postman环境来实现不同环境的接口调用? 2. api接口请求时往往会添加,来 ...
- Linux 系统的启动过程
1.基本概念 BIOS 中文名 主板BIOS 外文名 Basic Input/Output System 全 称 基本输入输出系统(全称是ROM-BIOS,是只读存储器基本输入/输出 ...