Java获得范型类型class
来源:互联网 发布:瑞士军刀淘宝能卖吗 编辑:程序博客网 时间:2024/05/23 15:06
Java很多框架或库中都会提供具有范型的回调接口或抽象类。当我们在使用时,代码类似于:
MyClient.getInstance().handleEvent(new MyCallback<SomeEntity>() { @Override public void onSuccess(SomeEntity entity) { System.out.println("Entity is: " + s); } @Override public void onFailure(String errorMsg) { System.out.println(errorMsg); } });
在匿名的回调类中,传入了范型类型,回调的接口中直接给出了相应类型的入参实例。
那么这样的功能是如何实现的呢?
实际是java的范型机制中,并没有把所有的范型信息擦除,通过一些反射相关的API,可以将父类的范型参数获取到。
首先假定我们的Callback是抽象类方式提供的:
public abstract class MyAbstractCallback<ENTITY> { abstract void onSuccess(ENTITY entity); abstract void onFailure(String errorMsg);}
在这里引入了范型,当我们实现这个抽象类,就是本文最开始的形式。虽然我们的onSuccess入参类型已经是相应类型实例了,
但我们的“库”在回调onSuccess函数时,并没有相应的实例。因此我们需要获取到“ENTITY”对应类的class对象,并使用反射的方法生成实例,回调给上层。
那么handleEvent的内部应该是这样的:
public <ENTITY> void handleEvent(MyCallback<ENTITY> callback) { Object obj = Class.forName(ENTITY.class.getName()).newInstance(); ENTITY result = (ENTITY) obj; callback.onSuccess(result); }
很显然这样的写法是无法编译通过的,因为我们无法通过范型类型ENTITY直接获取到它的class,在编译期java已经将范型擦除。
但由于callback对象必然是抽象类的一个实现子类,因此我们虽然不能直接获取到callback实现类中的范型信息,但可以获取到父类中的范型参数来达到同样的效果,这个信息是不会被擦除的。
java的反射中提供了getGenericSuperclass()
方法可以实现这个需求。
public <ENTITY> void handleEvent(MyAbstractCallback<ENTITY> callback) { // 单继承,父类只有一个 Type superclass = callback.getClass().getGenericSuperclass(); // 如果实现类没有范型信息,直接返回 if (!(superclass instanceof ParameterizedTypeImpl)) { return; } try { // 找到声明的具体类型 ParameterizedType parameterized = (ParameterizedType) superclass; Type actualType = parameterized.getActualTypeArguments()[0]; Object obj = Class.forName(actualType.getTypeName()).newInstance(); ENTITY result = (ENTITY) obj; onSuccessB(callback, result); } catch (Exception e) { e.printStackTrace(); onFailureB(callback, e.getLocalizedMessage()); } }
整个流程可以归纳为:
1.通过getGenericSuperclass()
方法获取到带有范型参数的父类信息,为Type的实例。
2.如果这个实例包含范型参数,那么获取到的是ParameterizedTypeImpl
实例。
3.将这个Type类型实例向下强转为ParameterizedType
类型,通过getActualTypeArguments
方法获取到实际的类型信息(这个方法返回的是数组,因为我们定义的抽象类仅有一个范型参数,因此数组第一个元素就是我们实现时传入的类型)。
4.获取到类型后就可以通过反射方法生成实例,调用回调方法返回。
对于接口类型的回调也同样适用,与获取父类范型参数类似,java提供了getGenericInterfaces
方法,返回所有实现的接口。
如果接口是带有范型参数的,那么它同样是ParameterizedTypeImpl
的实例。代码逻辑稍有增加,主要是因为一个类可以实现多个接口,需要在getGenericInterfaces
返回的数组里先找到相应的接口类型。
public <ENTITY> void handleEvent(MyCallback<ENTITY> callback) { Type[] interfaces = callback.getClass().getGenericInterfaces(); Type target = null; // 实际的对象可能实现了多个接口,需要从其中找到MyCallback for (int i = 0; i < interfaces.length; i++) { // 范型接口在数组中是ParameterizedTypeImpl类型 if (interfaces[i] instanceof ParameterizedTypeImpl) { ParameterizedTypeImpl impl = (ParameterizedTypeImpl) interfaces[i]; if (impl.getRawType().getName().equals(MyCallback.class.getName())) { // target就是实现了MyCallback的范型接口 target = interfaces[i]; } } } if (target != null) { try { // 找到声明的具体类型 ParameterizedType parameterized = (ParameterizedType) target; Type actualType = parameterized.getActualTypeArguments()[0]; Object obj = Class.forName(actualType.getTypeName()).newInstance(); ENTITY result = (ENTITY) obj; onSuccess(callback, result); } catch (Exception e) { e.printStackTrace(); onFailure(callback, e.getLocalizedMessage()); } } }
找到接口类型后适用同样的方法拿到具体类型并反射生成实例。
将这两个方法封装到一个类中:
public class MyClient { private static MyClient singleInstance = new MyClient(); public static MyClient getInstance() { return singleInstance; } /** * 处理接口类型的回调函数 * @param callback * @param <ENTITY> */ public <ENTITY> void handleEvent(MyCallback<ENTITY> callback) { Type[] interfaces = callback.getClass().getGenericInterfaces(); Type target = null; // 实际的对象可能实现了多个接口,需要从其中找到MyCallback for (int i = 0; i < interfaces.length; i++) { // 范型接口在数组中是ParameterizedTypeImpl类型 if (interfaces[i] instanceof ParameterizedTypeImpl) { ParameterizedTypeImpl impl = (ParameterizedTypeImpl) interfaces[i]; if (impl.getRawType().getName().equals(MyCallback.class.getName())) { // target就是实现了MyCallback的范型接口 target = interfaces[i]; } } } if (target != null) { try { // 找到声明的具体类型 ParameterizedType parameterized = (ParameterizedType) target; Type actualType = parameterized.getActualTypeArguments()[0]; Object obj = Class.forName(actualType.getTypeName()).newInstance(); ENTITY result = (ENTITY) obj; onSuccess(callback, result); } catch (Exception e) { e.printStackTrace(); onFailure(callback, e.getLocalizedMessage()); } } } private <ENTITY> void onSuccess(MyCallback<ENTITY> callback, ENTITY entity) { System.out.println("log success"); callback.onSuccess(entity); } private <ENTITY> void onFailure(MyCallback<ENTITY> callback, String msg) { System.out.println("log failure"); callback.onFailure(msg); } // -------------------------------------------- /** * 处理类形式的回调函数 * @param callback * @param <ENTITY> */ public <ENTITY> void handleEvent(MyAbstractCallback<ENTITY> callback) { // 单继承,父类只有一个 Type superclass = callback.getClass().getGenericSuperclass(); // 如果实现类没有范型信息,直接返回 if (!(superclass instanceof ParameterizedTypeImpl)) { return; } try { // 找到声明的具体类型 ParameterizedType parameterized = (ParameterizedType) superclass; Type actualType = parameterized.getActualTypeArguments()[0]; Object obj = Class.forName(actualType.getTypeName()).newInstance(); ENTITY result = (ENTITY) obj; onSuccessB(callback, result); } catch (Exception e) { e.printStackTrace(); onFailureB(callback, e.getLocalizedMessage()); } } private <ENTITY> void onSuccessB(MyAbstractCallback<ENTITY> callback, ENTITY entity) { System.out.println("log success"); callback.onSuccess(entity); } private <ENTITY> void onFailureB(MyAbstractCallback<ENTITY> callback, String msg) { System.out.println("log failure"); callback.onFailure(msg); }}
接下来可以写一些测试代码,可以通过修改构造函数参数数量,触发反射生成实例报错走onFailure的逻辑。这里由于反射仅生成了实例没有设置参数,所有的打印都是空值:
public class TestMain { public String name; // 打开用来测试newInstance报错走onFailure /*public TestMain(String name) { this.name = name; }*/ public String getName() { return name; } public void setName(String name) { this.name = name; } public void printTestMainName() { System.out.println("Name of TestMain is: " + this.name); } public static void main(String[] args) { MyClient.getInstance().handleEvent(new MyTestCallback()); MyClient.getInstance().handleEvent(new MyCallback<String>() { @Override public void onSuccess(String s) { System.out.println("String is: " + s); } @Override public void onFailure(String errorMsg) { System.out.println(errorMsg); } }); MyClient.getInstance().handleEvent(new AnotherCallback()); MyClient.getInstance().handleEvent(new MyAbstractCallback<String>() { @Override void onSuccess(String s) { System.out.println("String is: " + s); } @Override void onFailure(String errorMsg) { System.out.println(errorMsg); } }); }}
前两个方法是接口实现类的调用,后两个方法是抽象类的实现类调用。同时第一个测试方法传入的是事先定义好的实现类,第二次传入了匿名实现类。
接口与抽象类的定义:
public interface MyCallback<ENTITY> { void onSuccess(ENTITY entity); void onFailure(String errorMsg);}public abstract class MyAbstractCallback<ENTITY> { abstract void onSuccess(ENTITY entity); abstract void onFailure(String errorMsg);}
接口实现类与抽象类的实现类(这里主要测试了实现多个接口时的处理逻辑):
public class MyTestCallback implements MyCallback<TestMain>, FooInterface { @Override public void foo() { } @Override public void onSuccess(TestMain o) { o.printTestMainName(); } @Override public void onFailure(String errorMsg) { System.out.println(errorMsg); }}public class AnotherCallback extends MyAbstractCallback<TestMain> { @Override void onSuccess(TestMain testMain) { testMain.printTestMainName(); } @Override void onFailure(String errorMsg) { System.out.println(errorMsg); }}
- Java获得范型类型class
- Java获得泛型类型
- Java获得泛型类型
- 获得泛型(generic class)参数类型T的实际类型(actual type)
- [Java].class类类型
- 父类通过反射获得子类的class泛型类型
- Java获得Web容器类型
- 获得泛型的Class
- Java获得Class对象的方法
- Java的反射获得Class对象
- java 通过反射获得泛型的实际类型参数
- Java泛型-获得泛型的实际参数类型
- java中Class类型(1)
- Java获取泛型T的类型 T.class
- java反射,获得Class是否为基本数据类型,是否其他类的父类。获得泛型。
- 获得泛型类型的具体类型
- 获得Java类的方法类型签名
- 如何获得泛型的Class对象
- hdu-1045-Fire Net-二分图匹配-匈牙利算法-java
- int和Integer的区别 String和StringBuffer的区别
- ResNet 《Deep Residual Learning for Image Recognition》 阅读笔记
- leetcode 72. Edit Distance
- core-site.xml
- Java获得范型类型class
- JAVA软件设计思想之我见之架构设计(3)
- 设置事务模式及隔离级别
- org.apache.catalina.util.DefaultAnnotationProcessor cannot be cast to org.ap解决方案
- http://www.cnblogs.com/liangcheng11/p/6380922.html
- mybatis_study02
- chrome插件系列一:Secure Shell(替代ssh客户端)
- Centos7下redis安装phpredis扩展安装
- 2017暑假训练第三周周中总结