一、泛型概述
泛型:是JDK5中引入的一个新特性,泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型
泛型的本质是参数化类型,即给类型指定一个参数,然后在使用时再指定此参数具体的值,那样这个类型就可以在使用时决定了,这种参数类型可以用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法
在学习集合时,我们都知道集合中是可以存储
任意类型的对象
,只要把对象存储集合后,那么他们都会被提升为Object类型,当我们在取出每一个对象,并且进行相应的操作,这时我们必须采用类型转换
,这个时候就可以使用泛型解决该问题
二、泛型基本语法
Java1.7之前语法:
类名<具体的数据类型> 对象名 = new 类名<具体的数据类型>();
Java1.7以后语法:
// Java1.7以后,后面的<>中的具体数据类型可以省略不写
类名<具体的数据类型> 对象名 = new 类名<>();
三、泛型的好处
泛型的好处是在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,提高代码的重用率
- 泛型的作用
- 安全性
- 消除转换
- 提升性能
- 重用性
1、安全性
保证了类型的安全性
在没有泛型之前,从集合中读取到的每一个对象都必须进行类型转换,如果不小心插入了错误的类型对象,在运行时转换处理就会出错
没有泛型的情况下使用集合:
package cn.com.example11;
import java.util.ArrayList;
public class GenericClass {
public static void noGeneric(){
ArrayList arrayList = new ArrayList();
arrayList.add(1);
arrayList.add("admin");
// 编译正常
}
}
有泛型的情况下使用集合:
package cn.com.example11;
import java.util.ArrayList;
public class GenericClass {
public static void useGeneric(){
ArrayList<Integer> arrayList = new ArrayList<>();
arrayList.add(1);
arrayList.add("admin");
// 编译不通过
}
}
相当于告诉编译器每一个集合接收的对象类型是什么,编译器在编译期就会做类型检查,告知是否插入了错误类型的对象,使得程序更加安全,增强了程序的健壮性
2、消除转换
泛型的一个附带好处就是消除源代码中的许多强制类型转换,这使得代码更加可读,并且减少了出错机会
没有泛型的代码需要强制转换:
ArrayList arrayList = new ArrayList();
arrayList.add("admin");
String name = (String)arrayList.get(0);
使用泛型时代码不需要强制转换:
ArrayList<String> arrayList = new ArrayList<String>();
arrayList.add("admin");
String name = arrayList.get(0);
3、提升性能
在非泛型编程中,将简单类型作为Object传递时,会引起装箱和拆箱操作,这两个过程都是具有很大开销的,引入泛型后,就不必进行装箱和开箱,所以运行效率相对较高,特别在对集合操作非常频繁的系统中,这个特点带来的性能提升更加明显
4、重用性
提高了代码的重用性
四、泛型分类
泛型有三种使用方式:泛型类、泛型接口和泛型方法。
1、泛型方法
泛型方法:把泛型定义在方法上
语法格式:
public <泛型类型> 返回类型 方法名(泛型类型 变量名){
}
泛型方法
package cn.com.example11;
public class GenericMethod {
public static <T>T genericMethodHandler(T value){
return value;
}
public static void main(String[] args) {
System.out.println(genericMethodHandler(20) + 1); // 20
System.out.println(genericMethodHandler("20") + 1); // 201
}
}
2、泛型类
泛型类:把泛型定义在类上
定义泛型类,在类名后面添加一对尖括号,并在尖括号中填写类型参数,参数可以有多个,多个参数使用逗号分隔
后面的参数类型也是有规范的,通常类型参数我们都使用大写的单个字母表示
T:任意类型 type
E:集合中元素类型 element
K:key-value形式 key
V:key-value形式 value
语法格式:
public class 类名 <泛型类型1,...> {
}
注意:泛型类型必须是引用类型
泛型类:
package cn.com.example11;
public class GenericClass<T> {
private T name;
public GenericClass(){
}
public GenericClass(T name){
this.name = name;
}
public T getName(){
return name;
}
public void setName(T name){
this.name = name;
}
}
测试类:
GenericClass<String> genericClass = new GenericClass<>("admin");
System.out.println(genericClass.getName());
GenericClass<Integer> genericClass1 = new GenericClass<>(20);
System.out.println(genericClass1.getName());
3、泛型接口
泛型接口:定义在接口上
语法格式:
public interface 接口名<泛型类型>{
}
泛型接口
src/service/IGeneric.java
package cn.com.example11;
public interface IGeneric<T> {
void setValue(T value);
}
实现类
src/service/impl/GenericStringImpl.java
public class GenericStringImpl implements IGeneric<String>{
@Override
public void setValue(String value) {
System.out.println(value);
}
}
public class GenericIntegerImpl implements IGeneric<Integer>{
@Override
public void setValue(Integer value) {
System.out.println(value);
}
}
4、泛型接口范围扩大
泛型接口范围只在当前接口以及实现类中,如果想要把范围延伸到实现类以外的类中,那么需要进行接口范围扩大
泛型接口
public interface IGeneric<T>{
T getValue();
}
实现类
public class GenericImpl<T> implements IGeneric<T>{
public T name;
T getValue(){
return name;
}
}
实现类子类
// 第一种方式
public class Generic extends GenericImpl<String>{
@Override
public String getValue() {
return super.getValue();
}
}
// 第二种方式
public class Generic extends GenericImpl{
@Override
public String getValue() {
GenericImpl<String> genericImpl = new GenericImpl<>();
return super.getValue();
}
}
4、泛型通配符
泛型通配符是用于解决泛型之间引用传递问题的特殊语法,分别是:无边界的通配符、上边界的通配符【泛型上限】、下边界的通配符【泛型下限】
4.1、无边界通配符
<?>:表示参数类型可以是任意类型
public class Symbol<?>{
}
无边界通配符的主要作用就是让泛型能够接收未知类型的数据,和T、E、K、V不同的是,它们有对应的类型描述
4.2、上边界通配符
<? extends User>:表示参数类型必须是User类类型或User的子类类型
public class Symbol<? extends User>{
}
4.3、下边界通配符
<? super User>:表示参数类型必须是User类类型或User的父类类型
public class Symbol <? super User>{
}
五、泛型擦除
泛型擦除:通过反射机制可以添加指定类型【泛型】以外的数据类型
public class Generic{
public static void genericErase(){
// 实例化List集合,指定泛型String类型
List<String> list = new ArrayList<>();
list.add("admin");
list.add("user");
list.add("guest");
// 开始泛型擦除 - 往list集合中添加一个Integer类型的数据
// 1、获取该集合的Class对象
Class clazz = list.getClass();
// 2、获取指定的方法对象
Method method = clazz.getMethod("add",Object.class);
// 3、运行方法
method.invoke(list,20);
// 4、输出集合
System.out.println(list); // ["admin","user","guest",20]
}
}