近期工作中遇到一个情况,想要在构造器中直接获取泛型中传入的类,然后通过这个类来达到偷懒的效果。
普通的写法 MyClass<Person> myClass = new MyClass<>()
是无法正常获取的,只能获取到MyClass
在类定义时写的那个T
。
其实处理办法相当简单,就是在调用构造方法时,将写法改为:MyClass<Person> myClass = new MyClass<Person>(){}
就行。
如果对原理不感兴趣的同学,看到这里就可以快乐的撤退啦。
泛型构造器中的{}
实例化时,构造方法后写上{}
,代表通过子类继承的方式初始化MyClass
。废话不多说,见代码:
// 普通泛型类
public class MyDAO<T> {
}
// 测试类
public class MyDAOTest {
@Test
public 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.Class
Type genType = getClass().getGenericSuperclass();
// 这里的 params 就是泛型 T 对应的实际类
Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
System.out.println(params[0].getTypeName());
}
}
测试类就不重复写了,还是上面的MyDAOTest
。因为在构造器中进行了获取泛型的操作,具体输出如下
# 输出的内容就是Person类的全名。
com.zhqy.bean.Person
com.zhqy.bean.Person
泛型与protected搭配
这里要说的是一个小技巧,搭配使用可以在编辑期杜绝代码错误。因为protected
的特性所以可以通过子类的来避过调用问题,但自己写子类既繁琐又无法避免不会漏传具体类型,所以可以将子类生成的工作交给编译器。
详细看可以发现,MyDAO
的构造器是protected
,写上{}
后编辑器会生成对应的子类,不写的话会直接报错。这样就可以避免直接调用时具体类型被擦去的问题。