泛型&通配符常⻅⾯试题总结
12 ⼈点赞
上⼀篇
Java 数据类型常⻅⾯试题总结
下⼀篇
String 类常⻅⾯试题总结
所有评论(10)
引⽤原⽂:使⽤ ? extends xxx 声明的泛型参数只能调⽤ get() ⽅法返回 xxx 类型,调⽤…
引⽤原⽂:两者接收参数的范围不同。并且,使⽤ ? extends xxx 声明的泛型参数只能…
引⽤原⽂:T extends xxx 和 ? extends xxx ⼜有什么区别?T extends xxx ⽤于定义泛…
注册 或 登录 语雀进⾏评论
加⼊语雀,参与知识分享与交流
或 语雀进⾏评论
%
关于语雀 使⽤帮助 数据安全 服务协议 English 快速注册
Java 泛型(Generics) 是 JDK 5 中引⼊的⼀个新特性。使⽤泛型参数,可以增强代码的
可读性以及稳定性。
编译器可以对泛型参数进⾏检测,并且通过泛型参数可以指定传⼊的对象类型。⽐如
Ar
rayList<Persion> persons = new ArrayList<Persion>() 这⾏代码就指明了该
ArrayList 对象只能传⼊ Persion 对象,如果传⼊其他类型的对象就会报错。
并且,原⽣
List 返回类型是 Object ,需要⼿动转换类型才能使⽤,使⽤泛型后编
译器⾃动转换。
泛型⼀般有三种使⽤⽅式:泛型类、泛型接⼝、泛型⽅法。
1.泛型类:
如何实例化泛型类:
2.泛型接⼝ :
实现泛型接⼝,不指定类型:
实现泛型接⼝,指定类型:
3.泛型⽅法 :
使⽤:
⾃定义接⼝通⽤返回结果 CommonResult<T> 通过参数 T 可根据具体的返回类型
动态指定结果的数据类型
定义
Excel 处理类 ExcelUtil<T> ⽤于动态指定 Excel 导出的数据类型
构建集合⼯具类(参考
Collections 中的 sort , binarySearch ⽅法)。
......
Java 的泛型是伪泛型,这是因为 Java 在编译期间,所有的泛型信息都会被擦掉,这也就
是通常所说类型擦除 。
编译器会在编译期间会动态地将泛型
T 擦除为 Object 或将 T extends xxx 擦除
为其限定类型
xxx 。
因此,泛型本质上其实还是编译器的⾏为,为了保证引⼊泛型机制但不创建新的类型,减
少虚拟机的运⾏开销,编译器通过擦除将泛型类转化为⼀般类。
这⾥说的可能有点抽象,我举个例⼦:
再来举⼀个例⼦ : 由于泛型擦除的问题,下⾯的⽅法重载会报错。
原因也很简单,泛型擦除之后,
List<String> 与 List<Integer> 在编译以后都变
成了
List 。
既然编译器要把泛型擦除,那为什么还要⽤泛型呢?⽤ Object 代替不⾏吗?
这个问题其实在变相考察泛型的作⽤:
使⽤泛型可在编译期间进⾏类型检测。
使⽤
Object 类型需要⼿动添加强制类型转换,降低代码可读性,提⾼出错概
率。
泛型可以使⽤⾃限定类型如
T extends Comparable 。
桥⽅法( Bridge Method ) ⽤于继承泛型类时保证多态。
⚠
注意 :桥⽅法为编译器⾃动⽣成,⾮⼿写。
泛型的限制⼀般是由泛型擦除机制导致的。擦除为 Object 后⽆法进⾏类型判断
只能声明不能实例化 T 类型变量。
泛型参数不能是基本类型。因为基本类型不是
Object ⼦类,应该⽤基本类型对应
的引⽤类型代替。
不能实例化泛型参数的数组。擦除后为
Object 后⽆法进⾏类型判断。
不能实例化泛型数组。
泛型⽆法使⽤
Instance of 和 getClass() 进⾏类型判断。
不能实现两个不同泛型参数的同⼀接⼝,擦除后多个⽗类的桥⽅法将冲突
不能使⽤
static 修饰泛型变量
......
⽆法编译,因为 x 和 y 都会被擦除为 Object 类型, Object ⽆法使⽤ > 进⾏⽐较
⽆法编译,因为不能使⽤
static 修饰泛型 T 。
泛型类型是固定的,某些场景下使⽤起来不太灵活,于是,通配符就来了!通配符可以允
许类型参数变化,⽤来解决泛型⽆法协变的问题。
举个例⼦:
T 可以⽤于声明变量或常量⽽ ? 不⾏。
T ⼀般⽤于声明泛型类或⽅法,通配符 ? ⼀般⽤于泛型⽅法的调⽤代码和形参。
T 在编译期会被擦除为限定类型或 Object ,通配符⽤于捕获具体类型。
⽆界通配符可以接收任何泛型类型数据,⽤于实现不依赖于具体类型参数的简单⽅法,可
以捕获参数类型并交由泛型⽅法进⾏处理。
List<?> 和 List 有区别吗? 当然有!
List<?> list 表示 list 是持有某种特定类型的 List,但是不知道具体是哪种
类型。因此,我们添加元素进去的时候会报错。
List list 表示 list 是持有的元素的类型是 Object ,因此可以添加任何类
型的对象,只不过编译器会有警告信息。
在使⽤泛型的时候,我们还可以为传⼊的泛型类型实参进⾏上下边界的限制,如:类型实
参只准传⼊某种类型的⽗类或某种类型的⼦类。
上边界通配符
extends 可以实现泛型的向上转型即传⼊的类型实参必须是指定类型的
⼦类型。
举个例⼦:
类型边界可以设置多个,还可以对
T 类型进⾏限制。
下边界通配符
super 与上边界通配符 extends 刚好相反,它可以实现泛型的向下转
型即传⼊的类型实参必须是指定类型的⽗类型。
举个例⼦:
? extends xxx 和 ? super xxx 有什么区别?
两者接收参数的范围不同。并且,使⽤ ? extends xxx 声明的泛型参数只能调⽤ ge
t() ⽅法返回 xxx 类型,调⽤ set() 报错。使⽤ ? super xxx 声明的泛型参数
只能调⽤
set() ⽅法接收 xxx 类型,调⽤ get() 报错。
T extends xxx 和 ? extends xxx ⼜有什么区别?
T extends xxx ⽤于定义泛型类和⽅法,擦除后为 xxx 类型, ? extends xxx ⽤
于声明⽅法形参,接收 xxx 和其⼦类型。
Class<?> 和 Class 的区别?
直接使⽤ Class 的话会有⼀个类型警告,使⽤ Class<?> 则没有,因为 Class 是⼀个泛
型类,接收原⽣类型会产⽣警告
不能,因为 Node<Circle> 不是 Node<Shape> 的⼦类
可以编译,
ChildNode<Circle> 是 Node<Circle> 的⼦类
可以编译,
List<? extends Number> 可以往外取元素,但是⽆法调⽤ add() 添加
元素。
Java 官⽅⽂档 :
https://docs.oracle.com/javase/tutorial/java/generics/index.html
Java 基础 ⼀⽂搞懂泛型:https://www.cnblogs.com/XiiX/p/14719568.html
泛型
什么是泛型?有什么作⽤?
! "
泛型的使⽤⽅式有哪⼏种?
项⽬中哪⾥⽤到了泛型?
●
●
●
●
什么是泛型擦除机制?为什么要擦除?
●
●
●
什么是桥⽅法?
泛型有哪些限制?为什么?
●
●
●
●
●
●
●
●
以下代码是否能编译,为什么?
通配符
什么是通配符?有什么作⽤?
通配符 ?和常⽤的泛型 T 之间有什么区别?
●
●
●
什么是⽆界通配符?
●
●
什么是上边界通配符?什么是下边界通配符?
以下代码是否能编译,为什么?
参考
●
●
ArrayList<E> extends AbstractList<E>
//此处T可以随便写为任意标识,常⻅的如T、E、K、V等形式的参数常⽤于表示泛型
//在实例化泛型类时,必须指定T的具体类型
public class Generic<T>{
private T key;
public Generic(T key) {
this.key = key;
}
public T getKey(){
return key;
}
}
Generic<Integer> genericInteger = new Generic<Integer>(123456);
public interface Generator<T> {
public T method();
}
协变指的就是如果Child是Parent的⼦类,那么List<Child> 也应该是List<Parent> 的
⼦类,但是泛型是不⽀持的。
这⾥有笔误吧,两个 `? extends xxx`
上边界通配符指明了⽗类型,不确定⼦类型,因此只能从⽗类取数据,不能存数据;下
边界通配符指明了⼦类型,不确定⽗类型,因此只能⼦类存数据,不能⼦类取数据。
⽗类可以转换成⼦类,⼦类不能转换成⽗类。
表达不准确,理解意思就⾏。
List<? extends Integer> list = new ArrayList<>();
List<Integer> list1 = new ArrayList<>();
System.
out
.println((list.getClass() == list1.getClass())); 这样的结果是true,这⾥的
擦除为限定类型怎么理解呢,是因为都是List类型吗
《Thinking in java》⼀书中指出,由于泛型具有擦除机制,在运⾏时的类型参数会被擦
除,Java只知道存储的对象是⼀个Object⽽已,⽽对于Java的数组来说,他必须知道它
持有的所有对象的具体类型,⽽泛型的这种运⾏时擦除机制违反了数组安全检查的原
则。
⼤纲
12
10
登录 / 注册