什么是泛型:
jdk5以后出现的
泛型在定义类,接口和方法时使类型(类和接口)成为参数。与方法声明中使用的更熟悉的 形式参数非常相似,类型参数为你提供了一种使用不同输入重复使用相同代码的方法。区别在于形式参 数的输入是值,而类型参数的输入是类型。
泛型的好处:
1.可以在编译时进行更强的类型检查
2.通过使用泛型,程序员可以实现对不同类型的集合进行工作,可以自定义并且类型安全且易于阅读的泛型算法。
泛型有三种使用方式,分别为:泛型类、泛型接口、泛型方法
//泛型类
public class Box {
private T t;
//这个不是泛型方法
public void set(T t) {
this.t = t;
}
public T get() {
return t;
}
}
泛型接口
//泛型接口
interface Generics{
}
//泛型接口 多参数
interface GenericsN{
}
泛型方法
//泛型方法
public T test1(T t){
return t;
}
注意若在泛型类当中加入泛型方法,此处两个参数是不一样的。
最常见的类型变量名有:
E:元素(在Java集合框架中有广泛的应用)
K:键
N:数字
T:类型
V:值
S,U,V 等:第二,第三,第四个类型
通常我们
Foo中的T为类型参数
Foo中的String为类型变量
The Diamond钻石运算符
JDK7以下版本
Box integerBox = new Box();
JDK7及以上版本
Box integerBox1 = new Box<>();// The Diamond(菱形) 类型推断
功能:对泛型变量的范围作出限制
格式:
单一限制:
多种限制:
extends表达的意义:这里指的是广义上“扩展”,兼有“类继承”和“接口实现”之意
多种限制下的格式语法要求:如果上限类型是一个类,必须第一位标出,否则编译错误
泛型算法实现的关键:利用受限类型参数
// 多重限定
public class Test {
static class A{}
static class A1{}
static interface B{}
static interface C{}
// static class D{}//编译报错
//具有多个限定的类型变量是范围中列出的所有类型的子类型。如果范围之一是类,则必须首先指定它
static class D1{}//OK
**单继承 多继承报错**
// static class D2{}
}
受限参数类型有几种:
泛型中的问号符“?”名为“通配符”
受上下限控制的通配符
通配符的适用范围:
参数类型 、字段类型、局部变量类型、返回值类型(但返回一个具体类型的值更好)
语法格式:
优点:扩大兼容的范围
List要比List更加严格,因为前者仅能匹配XXX列表,然而后者却可同时匹配XXX及其子类的列表
功能:限定了类型的下限,也就它必须为某类型的父类
格式:
List比List要更加严格。因为前者仅仅兼容XXX类型的列表,而后者却兼容任何XXX超类的列表
List是List的一个子类型
理解List和List的不同:差在NULL处理,前者不支持,而后者却可接受一个null入表
Java泛型PESC原则
如果你只需要从集合中获得类型T , 使用通配符
如果你只需要将类型T放到集合中, 使用通配符
如果你既要获取又要放置元素,则不使用任何通配符。例如List
PECS即 Producer extends Consumer super, 为了便于记忆。
为何要PECS原则? 提升了API的灵活性
java虚拟机是不支持泛型的,kt也是伪泛型
功能:保证了泛型不在运行时出现
类型消除应用的场合:
编译器会把泛型类型中所有的类型参数替换为它们的上(下)限,如果没有对类型参数做出限制,那么就替换为Object类型。因此,编译出的字节码仅仅包含了常规类,接口和方法。
在必要时插入类型转换以保持类型安全。
生成桥方法以在扩展泛型时保持多态性
Bridge Methods 桥方法
当编译一个扩展参数化类的类,或一个实现了参数化接口的接口时,编译器有可能因此要创建一个合成方法,名为桥方法。它是类型擦除过程中的一部分
消除方法:同对泛型类的处理
无限制:替换为Object
有限制:替换为第一受限类型
可具体化类型和不可具体化类型的定义:
可具体化类型:就是一个可在整个运行时可知其类型信息的类型。
包括:基本类型、非泛型类型、原始类型和调用的非受限通配符。
不可具体化类型:无法整个运行时可知其类型信息的类型,其类型信息已在编译时被擦除:
例如:List和List,JVM无法在运行时分辨这两者
但是有疑问了?为什么还可以通过放射拿到对应的类型呢,不是已经被擦除了吗?
这里虽然对应的擦除了,但是泛型的信息保留在了类的常量池里。
堆污染:
发生时机:当一个参数化类型变量引用了一个对象,而这个对象并非此变量的参数化类型时,堆污染就会发生。
分模块对代码进行分别编译,那就很难检测出潜在的堆污染,应该同时编译
带泛型的可变参数问题:
T…将会被翻译为T[],根据类型擦除,进一步会被处理为Object[],这样就可能造成潜在的堆污染
避免堆污染警告
@SafeVarargs:当你确定操作不会带来堆污染时,使用此注释关闭警告
@SuppressWarnings({“unchecked”, “varargs”}):强制关闭警告弹出(不建议这么做)
泛型约束
无法利用原始类型来创建泛型
解决方法:使用它们的包装类
无法创建类型参数的实例
变通方案:利用反射就是可以
无法创建参数化类型的静态变量
原因:静态变量是类所有,共享决定了其必须能确定。但多个类型作为参数传入此类的多个实例时,就会无法确定究竟赋予此静态变量哪个实例对象所传入的参数了
无法对参数化类型使用转换或者instanceof关键字
但需要注意通配符的情况
无法创建参数化类型的数组
无法创建、捕获或是抛出参数化类型对象
但却可以在throw后使用类型参数
当一个方法的所有重载方法的形参类型擦除后,如果它们具有了相同的原始类型,那么此方法不可重载
原因:此情境下,类型擦除会产生两个同签名的方法
知识点:
1 Array中可以用泛型吗? 不能
2 你可以把List传递给一个接受List参数的方法吗? 不能
3 Java中List和原始类型List之间的区别?
原始类型和带参数类型之间的主要区别是,在编译时编译器不会对原始类型进行类型安全检查,却会对带参数的类型进行检查,通过使用Object作为类型,可以告知编译器该方法可以接受任何类型的对象,比如String或Integer。这道题的考察点在于对泛型中原始类型的正确理解。它们之间的第二点区别是,你可以把任何带参数的泛型类型传递给接受原始类型List的方法,但却不能把List传递给接受List的方法,因为会产生编译错误。
4Java中List和List之间的区别是什么?
实质上却完全不同。List 是一个未知类型的List,而List其实是任意类型的List。你可以把List, List赋值给List,却不能把List赋值给List
5 List和原始类型List之间的区别?
该题类似于“原始类型和带参数类型之间有什么区别”。带参数类型是类型安全的,而且其类型安全是由编译器保证的,但原始类型List却不是类型安全的。你不能把String之外的任何其它类型的Object存入String类型的List中,而你可以把任何类型的对象存入原始List中。使用泛型的带参数类型你不需要进行类型转换,但是对于原始类型,你则需要进行显式的类型转换
6 Java中的泛型是什么 ? 使用泛型的好处是什么?
泛型是一种参数化类型的机制。它可以使得代码适用于各种类型,从而编写更加通用的代码,例如集合框架。
泛型是一种编译时类型确认机制。它提供了编译期的类型安全,确保在泛型类型(通常为泛型集合)上只能使用正确类型的对象,避免了在运行时出现ClassCastException。
7 Java的泛型是如何工作的 ? 什么是类型擦除 ?
泛型的正常工作是依赖编译器在编译源码的时候,先进行类型检查,然后进行类型擦除并且在类型参数出现的地方插入强制转换的相关指令实现的。
编译器在编译时擦除了所有类型相关的信息,所以在运行时不存在任何类型相关的信息。例如List在运行时仅用一个List类型来表示。为什么要进行擦除呢?这是为了避免类型膨胀。伪泛型,虚拟机不支持
8什么是泛型中的限定通配符和非限定通配符 ?
限定通配符对类型进行了限制。有两种限定通配符,一种是它通过确保类型必须是T的子类来设定类型的上界,另一种是它通过确保类型必须是T的父类来设定类型的下界。泛型类型必须用限定内的类型来进行初始化,否则会导致编译错误。
另一方面表示了非限定通配符,因为可以用任意类型来替代。
9 如何阻止Java中的类型未检查的警告?
如果你把泛型和原始类型混合起来使用,例如下列代码,Java 5的javac编译器会产生类型未检查的警告
,例如List rawList = new ArrayList()
注意: Hello.java使用了未检查或称为不安全的操作;
这种警告可以使用@SuppressWarnings(“unchecked”)注解来屏蔽。
10 C++模板和java泛型之间有何不同?
java泛型实现根植于“类型消除”这一概念。当源代码被转换为Java虚拟机字节码时,这种技术会消除参数化类型。有了Java泛型,我们可以做的事情也并没有真正改变多少;他只是让代码变得漂亮些。鉴于此,Java泛型有时也被称为“语法糖”。
官方文档:https://docs.oracle.com/javase/tutorial/java/generics/index.html
中文翻译:https://pingfangx.github.io/java-tutorials/java/generics/types.html