近期工作中遇到一个情况,想要在构造器中直接获取泛型中传入的类,然后通过这个类来达到偷懒的效果。
普通的写法 MyClass<Person> myClass = new MyClass<>() 是无法正常获取的,只能获取到MyClass在类定义时写的那个T。
其实处理办法相当简单,就是在调用构造方法时,将写法改为:MyClass<Person> myClass = new MyClass<Person>(){}就行。
如果对原理不感兴趣的同学,看到这里就可以快乐的撤退啦。
泛型构造器中的{}
实例化时,构造方法后写上{},代表通过子类继承的方式初始化MyClass。废话不多说,见代码:
// 普通泛型类public class MyDAO<T> {}
// 测试类public class MyDAOTest {@Testpublic void testGetInstance() {MyDAO<Person> myClass = new MyDAO<Person>(){};// 相当于下面的MyDAO<Person> instance = new TestClassExtendMyDAO();}private static class TestClassExtendMyDAO extends MyDAO<Person> {}}
也可以通过编译器编译后的class文件来佐证这个理论。
如果MyDAOTest是普通的类,正常编译后,应该是有两个class文件 “MyDAOTest.class”、“MyDAOTest$TestClassExtendMyDAO.class”,但实际上编辑后多了一个 “MyDAOTest$1.class”文件。以此也可以说明,在测试类的代码中,生成了一个内部类。
// 这是 MyDAOTest$1.class 文件内容//// Source code recreated from a .class file by IntelliJ IDEA// (powered by FernFlower decompiler)//package com.zhqy.test;import com.zhqy.genericParadigm.MyDAOV2;class MyDAOTest$1 extends MyDAOV2 {MyDAOTest$1(final MyDAOTest this$0) {this.this$0 = this$0;}}
获取泛型的实际类
上面说的只是原理类的东西,下面详细记录一下如何获取泛型中实际类
// 普通泛型类public class MyDAO<T> {protected MyDAO() {// 必须有子类继承此类,否则这里获取到的是 java.lang.ClassType genType = getClass().getGenericSuperclass();// 这里的 params 就是泛型 T 对应的实际类Type[] params = ((ParameterizedType) genType).getActualTypeArguments();System.out.println(params[0].getTypeName());}}
测试类就不重复写了,还是上面的MyDAOTest。因为在构造器中进行了获取泛型的操作,具体输出如下
# 输出的内容就是Person类的全名。com.zhqy.bean.Personcom.zhqy.bean.Person
泛型与protected搭配
这里要说的是一个小技巧,搭配使用可以在编辑期杜绝代码错误。因为protected的特性所以可以通过子类的来避过调用问题,但自己写子类既繁琐又无法避免不会漏传具体类型,所以可以将子类生成的工作交给编译器。
详细看可以发现,MyDAO的构造器是protected,写上{}后编辑器会生成对应的子类,不写的话会直接报错。这样就可以避免直接调用时具体类型被擦去的问题。
