近期工作中遇到一个情况,想要在构造器中直接获取泛型中传入的类,然后通过这个类来达到偷懒的效果。
普通的写法 MyClass<Person> myClass = new MyClass<>() 是无法正常获取的,只能获取到MyClass在类定义时写的那个T
其实处理办法相当简单,就是在调用构造方法时,将写法改为:MyClass<Person> myClass = new MyClass<Person>(){}就行。

如果对原理不感兴趣的同学,看到这里就可以快乐的撤退啦。


泛型构造器中的{}

实例化时,构造方法后写上{},代表通过子类继承的方式初始化MyClass。废话不多说,见代码:

  1. // 普通泛型类
  2. public class MyDAO<T> {
  3. }
  1. // 测试类
  2. public class MyDAOTest {
  3. @Test
  4. public void testGetInstance() {
  5. MyDAO<Person> myClass = new MyDAO<Person>(){};
  6. // 相当于下面的
  7. MyDAO<Person> instance = new TestClassExtendMyDAO();
  8. }
  9. private static class TestClassExtendMyDAO extends MyDAO<Person> {
  10. }
  11. }

也可以通过编译器编译后的class文件来佐证这个理论。

如果MyDAOTest是普通的类,正常编译后,应该是有两个class文件 “MyDAOTest.class”、“MyDAOTest$TestClassExtendMyDAO.class”,但实际上编辑后多了一个 “MyDAOTest$1.class”文件。以此也可以说明,在测试类的代码中,生成了一个内部类。

  1. // 这是 MyDAOTest$1.class 文件内容
  2. //
  3. // Source code recreated from a .class file by IntelliJ IDEA
  4. // (powered by FernFlower decompiler)
  5. //
  6. package com.zhqy.test;
  7. import com.zhqy.genericParadigm.MyDAOV2;
  8. class MyDAOTest$1 extends MyDAOV2 {
  9. MyDAOTest$1(final MyDAOTest this$0) {
  10. this.this$0 = this$0;
  11. }
  12. }

获取泛型的实际类

上面说的只是原理类的东西,下面详细记录一下如何获取泛型中实际类

  1. // 普通泛型类
  2. public class MyDAO<T> {
  3. protected MyDAO() {
  4. // 必须有子类继承此类,否则这里获取到的是 java.lang.Class
  5. Type genType = getClass().getGenericSuperclass();
  6. // 这里的 params 就是泛型 T 对应的实际类
  7. Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
  8. System.out.println(params[0].getTypeName());
  9. }
  10. }

测试类就不重复写了,还是上面的MyDAOTest。因为在构造器中进行了获取泛型的操作,具体输出如下

  1. # 输出的内容就是Person类的全名。
  2. com.zhqy.bean.Person
  3. com.zhqy.bean.Person

泛型与protected搭配

这里要说的是一个小技巧,搭配使用可以在编辑期杜绝代码错误。因为protected的特性所以可以通过子类的来避过调用问题,但自己写子类既繁琐又无法避免不会漏传具体类型,所以可以将子类生成的工作交给编译器。

详细看可以发现,MyDAO的构造器是protected,写上{}后编辑器会生成对应的子类,不写的话会直接报错。这样就可以避免直接调用时具体类型被擦去的问题。