学习任务:

视频学习

动手测试类型擦除

我们使用反编译软件来探索泛型的底层原理,反编译即将class反编译为java文件。

反编译软件有很多,大家可以从网络获取,也可关注“攀博课堂”公众号进入“自学Java-->开发工具下载”找到反编译软件的下载地址。

image-20200919192319488

本教程使用XJad反编译软件,解压,打开XJad.exe文件:

image-20200919192501497

准备一个使用泛型的类:

package com.pbteach.javase.oop.generictypes;

/**
 * 数组操作通用类,使用泛型实现
 * @author 攀博课堂
 * @version v1.0
 */
public class PbArrayListV2<T> {
	
	//数组用于存储各种对象
	private static Object[] objects;

	public PbArrayListV2(int size){
		objects = new Object[size];
	}
	
	//添加元素
	public int add(T obj) {
		for(int i=0;i<objects.length;i++) {
			if(objects[i] == null) {
				//向objects存储元素
				objects[i] = obj;
				return 1;
			}
		}
		return 0;
		
	}
	
	//根据id获取元素
	public T getByIndex(int index) {
		if(index>=0 && index<objects.length) {
			return (T) objects[index];
		}
		return null;
	}
	
	public static void main(String[] args) {
		PbArrayListV2<PbCourse> arrayList = new PbArrayListV2<PbCourse>(10);
		arrayList.add(new PbCourse(101L, "攀博课堂 java面向对象编程高级篇v1.0", 20F));
		arrayList.add(new PbCourse(102L, "攀博课堂 Spring Cloud 微服务开发v1.0", 50F));
		//由于getByIndex方法返回Object所以需要强制转换
		PbCourse course = arrayList.getByIndex(1);
		System.out.println(course.getCourseName());
		
		//传入学生类型
		PbArrayListV2<PbStudent> studentList = new PbArrayListV2<PbStudent>(10);
		studentList.add(new PbStudent(101L, "传智燕青", "1759102882@qq.com"));
		studentList.add(new PbStudent(102L, "攀博", "pbteach@126.com"));
		PbStudent student = studentList.getByIndex(1);
		System.out.println(student.getNickName());
	}
}

找到class文件的位置,class文件在工程目录的bin目录下,找到bin目录下与包名对应的目录,使用反编译软件打开PbArrayListV2.class文件,软件即将class文件反编译为java文件,反编译后的代码如下:

// Decompiled by Jad v1.5.8e2. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://kpdus.tripod.com/jad.html
// Decompiler options: packimports(3) fieldsfirst ansi space 
// Source File Name:   PbArrayListV2.java

package com.pbteach.javase.oop.generictypes;

import java.io.PrintStream;

// Referenced classes of package com.pbteach.javase.oop.generictypes:
//			PbCourse, PbStudent

public class PbArrayListV2
{

	private static Object objects[];

	public PbArrayListV2(int size)
	{
		objects = new Object[size];
	}

	public int add(Object obj)
	{
		for (int i = 0; i < objects.length; i++)
			if (objects[i] == null)
			{
				objects[i] = obj;
				return 1;
			}

		return 0;
	}

	public Object getByIndex(int index)
	{
		if (index >= 0 && index < objects.length)
			return objects[index];
		else
			return null;
	}

	public static void main(String args[])
	{
		PbArrayListV2 arrayList = new PbArrayListV2(10);
		arrayList.add(new PbCourse(101L, "攀博课堂 java面向对象编程高级篇v1.0", 20F));
		arrayList.add(new PbCourse(102L, "攀博课堂 Spring Cloud 微服务开发v1.0", 50F));
		PbCourse course = (PbCourse)arrayList.getByIndex(1);
		System.out.println(course.getCourseName());
		PbArrayListV2 studentList = new PbArrayListV2(10);
		studentList.add(new PbStudent(101L, "传智燕青", "1759102882@qq.com"));
		studentList.add(new PbStudent(102L, "攀博", "pbteach@126.com"));
		PbStudent student = (PbStudent)studentList.getByIndex(1);
		System.out.println(student.getNickName());
	}
}

从反编译后的代码可看出T被替换为Object,Object是T的原始类型。编译器会最终把泛型标识符替换为原始类型,这种现象叫类型擦除。

如果泛型有限定类型则它的原始类型为限定类型,如下代码泛型的原始类型为PbCourse:

package com.pbteach.javase.oop.generictypes4;

/**
 * 课程服务类
 * 需求:支持PbCourse类及它的子类
 * @author 攀博课堂
 * @version v1.0
 */
public class PbCourseList<T extends PbCourse> implements PbList<T> {
	// 数组用于存储各种对象
	private Object[] objects;

	public PbCourseList(int size){
			objects = new Object[size];
		}

	/**
	 * 添加课程
	 * 
	 * @param obj
	 * @return
	 * @author 攀博课堂
	 * @version v1.0
	 */
	@Override
	public int add(T obj) {
		for (int i = 0; i < objects.length; i++) {
			if (objects[i] == null) {
				// 向objects存储元素
				objects[i] = obj;
				return 1;
			}
		}
		return 0;
	}

	/**
	 * 根据索引删除课程
	 * 
	 * @param index
	 * @return
	 * @author 攀博课堂
	 * @version v1.0
	 */
	@Override
	public T getByIndex(int index) {
		if(index>=0 && index<objects.length) {
			return (T) objects[index];
		}
		return null;
	}

	/**
	 * 根据id查询课程
	 * 
	 * @param id
	 * @return
	 * @author 攀博课堂
	 * @version v1.0
	 */
	@Override
	public T getById(long id) {
		for (int i = 0; i < objects.length; i++) {
			if (objects[i] != null) {
				T obj = (T) objects[i];
				if(obj.getId() == id) {
					return obj;
				}
			}
		}
		return null;
	}

}

getById(long id)方法反编译后的代码如下:

	public PbCourse getById(long id)
	{
		for (int i = 0; i < objects.length; i++)
			if (objects[i] != null)
			{
				PbCourse obj = (PbCourse)objects[i];
				if (obj.getId() == id)
					return obj;
			}

		return null;
	}

注意:如果限定类型有多个则将第一个类型作为原始类型去替换类型变量。

从上边反编译后的代码可以看出,类型擦出后编译器自动添加强制类型转换,如下:

PbCourse obj = (PbCourse)objects[i];
提问-攀博课堂
我要提问 不会就问,有效沟通
关注公众号,加入微信群交流提问。 攀博课堂官方公众号
问答列表,查看本知识点所有问题