2022-10-24
泛型 类型 定义
第五关 泛型的离奇失踪
小伙们看到这个标题,可能会大吃一惊,我们不是定义好泛型了吗,那么泛型还会突然离奇失踪啊。
我可以很负责的告诉小伙们: 泛型确实会消失!
1. 泛型的擦除机制(泛型消失)
我们定义的泛型类,泛型方法,只是编译时:规范类型参数的传递,然而在编译成class文件后,运行代码时,这时泛型就消失了。
原因就是:
在JAVA的虚拟机中并不存在泛型,泛型只是为了完善java体系,增加程序员编程的便捷性以及安全性而创建的一种机制,在JAVA虚拟机中对应泛型的都是确定的类型,在编写泛型代码后,java虚拟机中会把这些泛型参数类型都擦除,用相应的确定类型来代替,代替的这一动作叫做类型擦除,而用于替代的类型称为原始类型,在类型擦除过程中,一般使用第一个限定的类型来替换,若无限定,则使用Object.
下面我们测试一下:
//1.在Demo.java类中: 定义一个泛型的集合
Listlist = new ArrayList();
list.add("hello");
list.add("java");
//2.在编译后的Demo.class文件中,通过反编译查看:泛型消失了
List list = new ArrayList();
list.add("hello");
list.add("java");
2.泛型的擦除补偿
正如我们看到的,我们在代码运行时,泛型就消失了,那么如果我们在运行代码时需要确切的知道泛型的具体类型该怎么办呢?特别是使用new T(),或者使用instanceof, 因为这两类操作要知道确切的类型。
我们可以通过泛型擦除后的补偿来满足我们的需求,一般我们会采用java中的设计模式来解决这个问题。
方式一:简单工厂 (最简单)
在此方法中,将类型作为参数,以一个万能的工厂类(其中有一个返回具体类型类的实例的泛型方法)用类的
newInstance()方法返回参数化类型的实例,如下所示:
/**
* 定义泛型T
* @param <T>
*/
public class GenericDemo9<T> {
//1.定义泛型变量: t
private T t;
//2.定义方法:获取t
public T getInstance(Class<T> clz){
try {
this.t = clz.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return this.t;
}
//3.测试:
public static void main(String[] args) {
GenericDemo9<Date> gd = new GenericDemo9<Date>();
Date date = gd.getInstance(Date.class);
}
}
缺点:
因为class的newInstance()是利用无参构造方法创建对象的,如果该泛型类没有无参构造方法,就会报错
方式二:工厂方法(最灵活)
与简单工厂相比,工厂方法更灵活,同时来解决了简单工厂中没有无参构造方法,不能创建对象的问题。
如下所示:
//步骤一: 定义工厂接口
/**
* 定义一个泛型接口
* @param <T>
*/
public interface GenericFactory<T> {
T create();
}
//步骤二:定义具体创建对象的工厂
/**
* 定义生产汽车的工厂
*/
class CarFactory implements GenericFactory<Car>{
//1.定义汽车的名称
private String name;
//2. 定义汽车对象
private Car car;
public CarFactory() {
}
public CarFactory(String name) {
this.name = name;
}
@Override
public Car create() {
if(name==null){//没有汽车名称:表示使用无参数构造
this.car = new Car();
}else{//有汽车名称:表示使用有参数构造
this.car = new Car(this.name);
}
return car;
}
//3.测试
public static void main(String[] args) {
GenericFactory<Car> gf = new CarFactory();
Car car = gf.create();//使用无参构造创建对象
GenericFactory<Car> gf2 = new CarFactory("奔驰S500");
Car car2 = gf2.create();//使用有参构造创建对象
}
}
//步骤三:定义对象的类
//定义一个汽车类
class Car{
private String name;
public Car() {
}
public Car(String name) {
this.name = name;
}
}
缺点:代码实现起来麻烦
优点:创建对象的方式更加灵活,使用有参和无参构造都能创建对象。
模板方法(最简捷)与工厂方法不同的地方在于:用模板类(抽象类)来控制整个实例化过程的流程,本质就是用模板类控制对象的创建过程,具体创建对象的实现由模板类的子类去实现,只不过在模板类中需要用工厂方法。
如下所示:
//1.创建模板类
public abstract class GenericTemplate<T> {
//1.定义泛型变量
private T t;
//2.定义抽象方法
public abstract T create();
}
//2.创建模板类的生成者(实现类)
class CarCreator extends GenericTemplate<Car> {
//1.定义工厂对象:引入工厂方法
private CarFactory cf ;
public CarCreator() {
this.cf = new CarFactory();
}
public CarCreator(String carName) {
this.cf = new CarFactory(carName);
}
//2.重写模板类的方法
@Override
public Car create() {
return cf.create();
}
}
优点:
方式最简捷,因为直接调用具体的生成类即可,我们创建对象时,并看不到模板类的出现。
闯关练习
请描述 代码在运行过程中 泛型在擦除后,具体表示为什么类型?(单选)
A: Class类型
B: T类型
C: Object类型
D: Type类型
答案:C
解析:
在代码运行过程中,泛型会被擦除(也就是泛型会消失),这时泛型的类型通通都会表示为Object类型。
因为定义泛型时,可以指定任意类型,比如List<String>,Set<Number>,所以在泛型擦除后,只有Object类型可以表示任意类型。
开班时间:2021-04-12(深圳)
开班盛况开班时间:2021-05-17(北京)
开班盛况开班时间:2021-03-22(杭州)
开班盛况开班时间:2021-04-26(北京)
开班盛况开班时间:2021-05-10(北京)
开班盛况开班时间:2021-02-22(北京)
开班盛况开班时间:2021-07-12(北京)
预约报名开班时间:2020-09-21(上海)
开班盛况开班时间:2021-07-12(北京)
预约报名开班时间:2019-07-22(北京)
开班盛况Copyright 2011-2023 北京千锋互联科技有限公司 .All Right 京ICP备12003911号-5 京公网安备 11010802035720号