0
点赞
收藏
分享

微信扫一扫

关于JAVA 反射 基础知识/编码经验的一些总结


写在前面

  • 温习一下毕业以来学习的东西。准备做成一个系列。所以对于每一部分技术点进行一个笔记整理。更多详见 ​​java面试的一些总结​​
  • 笔记主要是以网上开源的一本​​《Java核心面试知识整理》​​​面试笔记为原型,结合​​工作中学习​​​的知识。​​《Effective Java》​​​、​​《编写高质量代码(改善Java程序的151个建议)》​​这两本书为方向进行整理。
  • 笔记立足​​DevOps​​​。开发+运维+测试三个方向 ,面向对​​JAVA有一定了解​​​的小伙伴用于​​温习​​​,因为​​理论较多一点​​。在不断更新,博文内容理解不足之处请小伙伴留言指正

傍晚时分,你坐在屋檐下,看着天慢慢地黑下去,心里寂寞而凄凉,感到自己的​生命被剥夺​​了。当时我是个年轻人,但我害怕这样生活下去,衰老下去。在我看来,这是​​比死亡更可怕的事​​。--------王小波

二、JAVA 反射

​动态语言​

动态语言,是指程序在运行时可以​​改变其结构​​​:新的函数可以引进,已有的函数可以被删除等结构上的变化。比如常见的 ​​JavaScript 就是动态语言​​​,除此之外 Ruby,​​Python 等也属于动态语言​​​,而 C、C++则​​不属于动态语言​​​。从反射角度说 ​​JAVA 属于半动态语言​​。

反射机制概念(运行状态中知道类所有的属性和方法)

​反射(Reflection)机制​​​允许程序在运行时借助​​Reflection API​​​取得任何​​类​​​的​​内部信息​​​,并不能直接操作对象的内部属性及方法。反射被视为​​动态语言的关键​​。

​白话讲解​​​:在 Java 中的​​反射机制​​​是指在​​运行状态​​​中,对于​​任意一个类​​​都能够知道这个类​​所有的属性和方法​​​;并且对于​​任意一个对象​​​,都能够调用它的​​任意一个方法​​​;这种动态获取信息以及动态调用对象方法的功能成为​​Java 语言的反射机制​​。

JAVA 反射 API

如果面试被问到,对于反射API了你了解多少?怎么回答。

​Reflection API​​​提供了​​Constructor​​​,​​Field​​​和​​Method​​​类,这三个类定义在​​java.lang.reflect​​​包中,分别用于描述类的​​构造方法​​​,​​属性​​​和​​方法​​​。​​JAVA8​​​之后在在​​java.lang.reflect​​​包中添加了​​Executable​​​抽象类,​​parameter​​​l类这两个API,​​Executable​​​描述类的​​执行对象​​​,​​parameter​​描述方法参数信息。

当然,获取这些反射APl的实例信息,还需要一个最重要对象​​元类Class​​​,​​java.lang.Class​​​类封装一个对象和接口运行时的状态。当​​类加载​​​时​​Class​​​类型的对象​​自动创建​​​,​​Class​​​没有​​公共构造方法​​​,其对象是​​JVM​​​在类加载时通过类加载器中的​​defineClass()​​​方法自动构造的,不能​​显示​​​的实例化一个​​class对象​​。

如果面试被问到你刚才说了​类加载​,你可以讲讲类加载么?

所谓​​类加载​​​,就是指将​​类​​​的​​class文件​​​读入​​内存​​​,并为之创建一个​​Java.lang.class对象​​​。即当​​线程​​​使用任何一个类时,系统都会为之创建一个​​java.lang.Class​​​对象。​​java​​​可以使用使用​​forName动态加载类文件​​​,​​动态加载(Dynamic Loading)​​​是指在程序​​运行时​​​加载需要的​​类库文件​​​,对Java程序来说,一般情况下,一个​​类文件​​​在​​启动​​​时或​​首次初始化​​​时会被​​加载到内存中​​​,而反射则可以在运​​行时决定​​​是否要​​加载一个类​​​,一个类文件只有在被加载到内存中才可能生成实例对象,即加载到内存中,生成​​Class​​​对象,通过​​new​​​关键字生成​​实例对象​​。

每个​​类被加载​​​之后,会生成一个​​Class对象​​​,通过​​Class对象​​​可以访问​​JVM​​​中该类的信息,一旦类被载入​​JVM中​​​,​​同一个类​​​将不会被​​再次载入​​​,被载入的类都有一个​​唯一的标识​​​,是该​​类​​​得到​​全限定类名(包括包名和类名)​​。

Class

Class常用方法:

如果面试被问到你对​Class对象​了解多少,说几个常用方法,怎么回答?

Class常用方法

描述

​static Class forName(String className)​

返回​​指定类名​​​的​​Class​​对象

​T newInstance()​

调用默认的构造方法,返回该Class对象的​​一个实例​

​String getName()​

返回​​Class​​​对象锁对应的​​类名​

构造器相关方法

描述

​Constructor<?>[] getConstructors()​

返回​​Class​​​对象所对应类的​​所有public构造方法​

​Constructor<T>getConstructors(Class<?>...parameterType)​

返回Class对象所对应的类的指定参数列表的​​public构造方法​

​Constructor<?>[]getDeclaredConstructors()​

返回​​Class​​​对象所对应的​​所有构造方法​​​,与​​访问权限无关​

​Constructor<T>getDeclaredConstructors(Class<?>...parameterTypes)​

返回​​Class对象​​​所对应类的​​指定参数列表​​​的所有​​构造方法​​,

与​​访问权限无关​​,如果使用私有构造器

构造,需要开启访问权限​​setAccessible(true)​​:

设置通过反射访问该成员变量时取消访问权限检查。

方法相关方法

描述

​Method[] getMethod()​

返回​​Class​​​对象所对应类的​​所有public方法​

​Method getMethod(String name,Class<?>...parameterType)​

返回​​Class​​​对象所对应的​​指定参数列表​​​的​​public方法​

​Method[] getDeclaredMechods()​

返回​​Class​​​对象所对应的​​所有方法​​​,与​​访问权限无关​

​Method getDeclaredMethod(String name,Class<?>...parameterTypes)​

返回​​Class​​​对象对应类的​​指定参数列表​​​的方法,与​​访问权限无关​

属性相关方法

描述

​Field[] getFields()​

返回​​Class​​​对象所对应类的所有​​public成员变量​

​Field getField(String name)​

返回​​Class​​​对象所对应的类的​​指定参数​​​的​​public成员变量​

​Field[] getDeclaredFields( )​

返回​​Class​​​对象所对应类的所有​​成员变量​​​,与​​访问权限无关​

​Field getDeclaredField(String name)​

返回​​Class​​​对象所对应类​​指定参数​​​的​​成员变量​​​,与​​访问权限无关​

这里需要注意的是(​Method​​、​​Constructor​​的也一样):

  • 调用​​getDeclaredFields()​​​方法可以获取包括​​私有​​​和​​受保护​​​的​​所有属性​​​,但​​不包括​​​父类的​​属性​​;
  • 调用​​getField()​​​方法可以获得所有的​​public属性​​​。包括​​从父类继承​​的。

注解相关方法

描述

​Annotation [] getAnnotation()​

返回​​Class对象​​​所对应类上存在的所有​​注解​

​< A extends Annotation>A getAnnotation(Class < A >annotationClass )​

返回​​Class对象​​​所对应类上存在的指定类型的​​注解​

Class自身信息相关方法

描述

​Class<?>getDeclaringClasses()​

返回​​Class​​​对象所对应的​​外部类​

​Class<?>[] getDeclaredClasses()​

返回​​Class​​​对象所对应的类里包含的所有​​内部类​

​Class<? super T>getSuperclass()​

返回​​Class​​​对象所对应的类里的​​父类的Class对象​

​int getModifiers()​

返回​​Class​​​对象所对应类的​​修饰符​​,返回的整数是修饰符的对应常量,需要是使用Modified工具类解码

​Class [] getInterfaces()​

返回​​Class​​​对象所对应类实现的​​所用接口​

​Class LoadergetClassLoader()​

返回该类的​​类加载器​

包相关方法

描述

​Package getPackage()​

返回​​Class对象​​​所对应的​​包​

Class谓词相关方法

描述

​boolean isArray()​

判断​​Class​​​对象是否表示一个​​数组类​

​boolean isEnum()​

判断​​Class​​​对象是否表示一个​​枚举​

​boolean isInterface()​

判断​​Class​​​对象是否表示一个​​接口​

​boolean isInstance(Object obj)​

判断​​obj对象​​​是否是该​​Class对象​​​的一个​​实例​

​boolean isAnnottation()​

返回​​Class​​​对象是否标识一个​​注解类型​

获取​​Class对象​​的四种方式:

如果面试被问到如何获取Class对象,怎么回答?

  1. 使用​​Class类​​​的​​forName(String classNmae)​​​静态方法,参数​​class​​​代表所需要​​类的全限定类名​​​。​​forName()​​​方法声明抛出​​ClassNotFoundException​​​受检异常,调用必须​​捕获或抛出异常​​。

Class<?> reflection_demoClass = Class.forName("com.liruilong.Reflection.Reflection_Demo");

  1. 调用某个类的​​class属性​​​来获取该类对应的​​Class对象​​​,​​对象.class​​​;类的​​Class属性​​​获得该类所对应的​​Class对象​​​,会始代码​​更安全​​​。​​程序性更好​​​。​​string​​​类型的字符串不能使用​​String.class​​​方式。需要使用​​Class.forName(“java.lang.String”)​​​,​​Object​​​类的​​.class​​文件默认是不包含参数信息的。

Class<Reflection_Demo> reflection_demoClass = Reflection_Demo.class;

  1. 调用某个类的​​getclass()​​​方法来获取该类对应的​​class对象​​​,该方法是​​Object类中的一个方法​

Class<? extends Reflection_Demo> aClass = new Reflection_Demo().getClass();

  1. 调用元类Class对应的​​getClassLoader()​​​获取类加载器,​​ClassLoader​​,这个有点牵强,姑且算一种吧,嘻嘻。。

ClassLoader classLoader = Reflection_Demo.class.getClassLoader();
Class<?> bClass = classLoader.loadClass("com.liruilong.Reflection.Reflection_Demo");

下面我们看看剩下的API吧!

Executable

​Executable抽象类​​​:​​JAVA8​​​在​​java.lang.reflect​​​包下新增了一个​​Executable​​​抽象类,代表可执行的类成员。​​Executable​​​抽象类派生了​​Constructor​​​和​​Method​​​两个子类。​​Executable​​​抽象类提供了大量方法来获取​​参数​​​,​​修饰符​​​或​​注解​​等信息。

方法

描述

​parameter [] getparameters()​

获取​​所有形参​​,返回一个parameter [] 数组

​int getParameterCount()​

获取​​形参个数​

​abstract int getModifiers()​

获取​​修饰符​​,返回的整数是修饰符关键字对应的常量

​boolean isVarArgs()​

判断是否包含数量​​可变的形参​

Constructor

​Constructor类​​​:用于表示类的​​构造方法​​​。通过​​Class​​​的​​getConstructor()​​​方法来获取​​构造方法​​​的​​集合​​。

方法

描述

​String getName()​

返回构造器的名称

​Class [] getParameterTypes()​

返回当前构造方法的参数类型

​int getModifiers()​

返回​​修饰符​​​的​​整型标识​​,返回的整数是修饰符是标识常量,需要使用Modified工具类方法解码Modified.toSting(int mod),可以通过Modified.PUBLIC 查看对应的值

Method

​Method类​​​:用于封装方法的信息,调用​​Class​​​对象的​​getMethods()​​​方法或​​getMethod()​​可以获取当前类的所有方法或指定的方法。

常用方法

功能描述

​String getName()​

返回方法的​​名称​

​Class[] getparameterType()​

返回方法的​​参数类型​

​int getModifieds()​

返回​​修饰符​​​的​​整型标识​

​Class getReturnType()​

返回当前方法的​​返回类型​

Field

​Field类​​​:用于封装属性信息,调用​​Class​​​对象的​​getFields()​​​或​​getField()​​​方法可以获取当前类的​​所有属性​​​或​​指定属性​​。

常用方法

描述

​String getName()​

获取属性的名称

​int getMOdifiers()​

返回修饰符的整型标识

​getXxx(Object obj)​

获取属性的值,此处的Xxx对应的java8中的基本类型,如果属性是引用类型,直接使用get(Object obj)方法

​setXxx(Object obj,Xxx val)​

设置属性的值,此处的Xxx对应Java8中的基本类型,如果属性是引用类型,直接使用set(Object obj,Object val)方法

​Class [] getType()​

返回当前属性的类型

parameter

​parameter类​​:是JAVA8中新增的API,每个paramtete 对象代表一个参数。Parameter类提供许多方法来获取参数信息

方法

功能

​int getModifiers()​

获取参数的​​修饰符​

​String getName()​

获取参数的​​形参名​

​Type getparameterizedType()​

获取​​带泛型​​​的​​形参类型​

​Class<?>getType()​

获取​​形参类型​

​boolean isVarArgs()​

判断该参数是否为​​可变参数​

​boolean isNamePreaent()​

判断​​.class​​文件中是否包含方法的形参名信息

利用反射创建​​对象​​的的两种方式:

如果面试被问到使用反射如何创建对象,怎么回答?

  • ​Class 对象的 newInstance()​​​: 使用 ​​Class​​​对象的 ​​newInstance()​​​方法来创建该​​Class​​​ 对象对应类的实例,但是这种方法要求该 ​​Class​​​对象对应的​​类有默认​​​的​​空构造器​​。

Class<?> reflection_demoClass = Class.forName("com.liruilong.Reflection.Reflection_Demo");
Reflection_Demo o = (Reflection_Demo) reflection_demoClass.newInstance();

System.out.println(o);// com.liruilong.Reflection.Reflection_Demo@677327b6

  • 调用​​Constructor​​​对象的​​newInstance()​​​: 先使用 ​​Class​​​对象获取指定的 ​​Constructor​​​对象,再调用​​Constructor​​​对象的​​newInstance()​​​方法来创建 ​​Class​​​ 对象对应类的实例,通过这种方法可以​​选定构造方法创建实例​​。

Class<?> reflection_demoClass = Class.forName("com.liruilong.Reflection.Reflection_Demo");
Constructor<?> constructors = reflection_demoClass.getConstructor(null);
Reflection_Demo o1 = (Reflection_Demo) constructors.newInstance(null);

System.out.println(o1); // com.liruilong.Reflection.Reflection_Demo@14ae5a5

关于反射的一些其他编码经验:

如果面试问关于反射,平常开发中有哪些经验,要怎么回答?

  • 注意Class类本身的特殊性:Java语言把​​Java源文件编译​​​为后缀为​​class​​​的​​字节码文件​​​,然后通过​​ClassLocale​​​机制把类文件​​加载​​​到​​内存​​​中,最后​​生成实例执行​​​,Java使用​​元类(MetaClass)​​​来描述​​加载​​​到​​内存中​​​的​​类数据​​​,即​​Class类​​​,描述​​类​​​的​​类对象​​​,需要注意​​Class的一些特性​​。
  • ​无构造函数​​,不能主动实例化,Class对象在加载时由java虚拟机通过类加载器中的defineClass自动构造。
  • ​可以描述基本类型 Class as=int.class;​​​8个基本类型执行​​JVM​​​中并不是​​一个对象​​​,一般​​存在​​​于​​栈中​​​,通过​​Class​​​可以​​描述​​​它们,可以使用​​int.calss​​​描述​​int类型​​​的​​类对象​​。
  • ​Class对象都是单例模式​​​,一个Class对象描述​​一个类​​​,只描述一个类,即一个类只有​​一个Class对象​​​。​​Class是java 的反射入口​​​,只有在获得一个类的​​动态描述​​​时才能​​动态的加载调用​​。
  • 适时选择​getDeclaredXXX​​和​​getXXX​​​:​​getDeclaredMethod​​​方法获得的是所有​​public访问级别​​​的方法,包括从​​父类继承来​​​的方法,而​​getDeclareMethod​​​获得自身类的​​所有方法​​​,包括​​公有的(public)​​​,​​私有(private)​​​,方法等,不受​​访问权限限制​​​。如果需要​​列出​​​所有​​继承自父类的方法​​​,可以​​先获得父类​​​,然后​​使用getDeclareMethods​​​,之后​​持续递归​​。
  • 反射访问属性​方法​时将​Accessible​设置为​true​,​​java​​​中通过​​反射执行方法​​​的步骤,获取一个​​对象的方法​​​,然后根据​​isAccessible​​​返回值确定是​​否能执行​​​,如果返回​​false​​​,则需要调用​​setAccessible(true)​​​,在调用​​invoke​​执行方法。
  • ​Access​​​并不是​​语法层次​​​理解的​​访问权限​​​,而是指​​是否更容易获得​​​,​​是否进行安全检查​​​。动态的​​修改​​​一个类或方法或执行方法时都会受到​​Java安全体系的制约​​​,而​​安全处理​​​非常​​消耗资源​​​,所以对于​​运行期​​​要执行的方法或修改的属性就​​提供了Accessible可选项​​​,由开发者决定是否要​​逃避安全体系的检查​​。
  • ​AccessibleObject​​​是​​field​​​,​​Method​​​,​​constructor​​​的​​父类​​​,​​决定​​​其​​是否​​​可以​​快速访问​​​而不进行​​访问控制检查​​​,​​AccessobleObject​​​类中是以​​override​​变量保存该值的。
  • ​Accessible​​​属性只是用来判断是否需要进行安全检查的,如果不需要则直接执行,可以​​提升系统性能​​​. ​​AccessibleObject​​​的其他两个子类​​field​​​和 ​​constructor​​​也相似,所以要设置​​Accessible​​​为​​true​​。

Method  method= genericDemo.class.getMethod("toArray");
if(!method.isAccessible())
method.setAccessible(true);
method.invoke(obj, args);

public class Foo {
public final void doStuff() {
System.out.println("Do liruilong ___$# ^_^");
}
public static void main(String[] args) throws Exception, Throwable {
Method method = Foo.class.getMethod("doStuff");
System.out.println("可以访问吗!!"+method.isAccessible());
method.invoke(new Foo());
}

  • 动态加载不适合数组,当使用forName加载一个类时,8个基本类型排除,它不是一个具体的类,还要具有可追索的类路径,否则包ClassNotFoundException异常。数组虽然是一个类,但没有定义类路径,可以加载编译后的对象动态动态加载一个对象数组,但是没有意义。在java中数组是定长的,没有长度的数组是不允许存在的。可以使用Array数组反射类来动态加载一个数组。

//动态创建一个数组
String [] strs = (String[]) Array.newInstance(String.class,8);
int[][] ints = (int [][])Array.newInstance(int.class,2,3);

元素类型

编译后的类型

byte[]

[B

char[]

[C

Double[]

[D

Float[]

[F

Int[]

[I

Long[]

[J

Short[]

[S

Boolean

[Z

引用类型(如String)

[L引用类型

  • 动态可以让代理模式更灵活,java的反射框架提供了动态代理(Dynamic Proxy)机制,允许在运行期对目标对象生成代理,
    静态代理:通过代理主题角色和具体主题角色共同实现抽象主题角色的逻辑的,只是代理主题角色把相关的执行逻辑委托给了具体主题角色而已。

静态代理:

interface  subject{
public void request();
}
class RealSubject implements subject{
public void request(){

}
}
class Proxy implements subject{
private subject subjects = null;
public Proxy(){
subjects = new RealSubject();
}
public Proxy(subject subjects){
this.subjects =subjects;
}
public void request(){
befoer();
subjects.request();
afert();
}
public void befoer(){}
public void afert(){}
}

java基于java.lang.reflect.​​Proxy​​​用于实现动态代理,使​​SubjectHandler​​​作为主要的逻辑委托对象,​​invoke​​​是必须要实现的,完成对真实方法的调用。即通过​​InvocationHandler​​​接口的​​实现类来实现​​​,所有被代理的方法都是由​​InvocationHandler​​​接管实际的​​处理任务​​。

动态代理

interface  subject{
public void request();
}
class RealSubject implements subject{
public void request(){

}
}
class SubjectHandler implements InvocationHandler{
private subject subjects;

private SubjectHandler(subject subjects) {
this.subjects = subjects;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("预处理");
Object obj = method.invoke(subjects,args);
System.out.println("后处理");
return obj;
}
}
//场景类
public static void main(String[] args) {

subject subjects = new RealSubject();
InvocationHandler handler = new SubjectHandler(subjects);
//当前加载器
ClassLoader cl = subjects.getClass().getClassLoader();
//动态代理
subject proxy = (subject) Proxy.newProxyInstance(cl,subjects.getClass().getInterfaces().request(),handler);
//执行具体的角色方法
proxy.request();
}

  • 使用反射增加装饰模式的普遍性,装饰模式:动态的给一个对象添加一些额外的职责。使用动态代理可以实现装饰模式。

//动物
interface Animal{
public void doStuff();
}
//老鼠
class Rat implements Animal{
@Override
public void doStuff(){
System.out.println("Jetty Tom");
}
}
//定义某种能力
interface Featuer{
public void load();
}

class FlyFeatuer implements Featuer{
public void load(){
System.out.println("增加 一支翅膀");
}
}
class DigFeatuer implements Featuer{
public void load(){
System.out.println("增加钻地能力!");
}
}

class DecorateAnimal implements Animal{
private Animal animal;
private Class<? extends Featuer> clz;
public DecorateAnimal(Animal animal,Class<? extends Featuer> clz){
this.animal = animal;
this.clz = clz;
}
@Override
public void doStuff(){
InvocationHandler handler = new InvocationHandler() {
//具体的包装行为
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object obj = null;
//设置包装
// public class Modifier
// extends ObjectModifier类提供了static方法和常量来解码类和成员访问修饰符。 修饰符集合被表示为具有表示不同修饰符的不同位位置的整数。
//method.getModifiers()返回由该对象表示的可执行文件的Java语言modifiers 。
if (Modifier.isPublic(method.getModifiers())){
obj = method.invoke(clz.newInstance(),args);
}
animal.doStuff();
return obj;
}
};
ClassLoader cl = getClass().getClassLoader();
Featuer Proxy = (Featuer) java.lang.reflect.Proxy.newProxyInstance(cl,clz.getInterfaces(),handler);
Proxy.load();
}
}

public class Demo {
public static void main(String[] args) {
//定义Jerry老树
Animal Jerry = new Rat();
Jerry = new DecorateAnimal(Jerry,FlyFeatuer.class);
Jerry = new DecorateAnimal(Jerry,DigFeatuer.class);
Jerry.doStuff();

}
}

  • 反射让模板方法模式更强大,模板方法模式:定义一个操作中的算法骨架,将一些步骤延迟到子类中,使子类不改变一个算法的结构即可定义该算法的某些特定的步骤,即父类定义抽象模板为骨架,其中包括基本方法(由子类实现的方法,并且在模板方法被调用)和模板方法(实现对基本方法的调用,完成固定的逻辑)

public abstract class AbsPopulator{
public final void dataInitialing() throws Exception{
doInit();
}
protected abstract void doInit();
}
public class UserPopulator extends AbsPopulator{
protected void doInit(){}
/*初始化用户表,如创建,加载数据等*/
}

基于反射增强的模板方法

public abstract class AbsPopulator{
public final void dataInitialing() throws Exception{
//获得所有public方法
Method[] methods = getClass().getMethods();
for(Method m:methods){
//判断是否是数据初始化方法
if(isIinitDataMethod(m)){
m.invoke(this);
}
}
}

private boolean isIinitDataMethod(Method m) {
return m.getName().startsWith("init")&&//init开始
Modifier.isPublic(m.getModifiers())&&//公开的方法
m.getReturnType().equals("Void.Type")&&//返回类型为void
!m.isVarArgs()&&//输入参数不为空
!Modifier.isAbstract(m.getModifiers());//不能为抽象方法
}
}
public class UserPopulator extends AbsPopulator{
public void Inituser(){}
public void InitPassword(){}
public void InitJobz(){}
public void Inituser(){}
/*初始化用户表,如创建,加载数据等*/

}

使用反射后,不需要定义任何抽象方法,只需要定义一个基本的方法鉴别器,即可加载否和规则的基本方法,模板方法根据鉴别器返回执行相应的方法。

  • 不需要太多的关注反射效率,获得一个泛型类的实际泛型类型。这个Demo有点问题,以后再研究

class Utils{
//获得一个泛型类的实际泛型类型
public static <T> Class<T> getGenricClassType(Class clz) {
//返回 Type表示此所表示的实体(类,接口,基本类型或void)的直接超类 类
Type type = clz.getGenericSuperclass();
//ParameterizedType表示一个参数化类型,如Collection <String>。
if(type instanceof ParameterizedType){
ParameterizedType pt = (ParameterizedType){
ParameterizedType pt = (ParameterizedType)type;
Type[] types = pt.getActualTypeArguments();
if (types.length>0&&types[0] instanceof Class){
return (Class)types[0];
}
}
return (Class)Object.class;
}
}
}


举报

相关推荐

0 条评论