反射、注解和泛型遇到重载和继承时注意事项
反射、注解和泛型遇到OOP时需要注意的问题
反射调用方法不是以传参决定重载
在获取方法的时候通过方法名和参数类型来确定的。遇到方法有包装类型和基本类型重载的时候,需要注意 @Slf4j public class ReflectionIssueApplication { private void age(int age) { log.info("int age = {}", age); } private void age(Integer age) { log.info("Integer age = {}", age); } } //使用getDeclaredMethod获取age方法,输出int age = 36 //这里使用Integer.valueOf("36")结果一样 getClass().getDeclaredMethod("age", Integer.TYPE).invoke(this, 36); //使用反射,以反射获取方法时传入的方法名称和参数类型确定调用方法 getClass().getDeclaredMethod("age", Integer.class).invoke(this, Integer.valueOf("36")); getClass().getDeclaredMethod("age", Integer.class).invoke(this, 36); //输出结果都是Integer age = 36
反射获取类成员
注意 子类重写父类方法时,泛型子类要指定类型否则会出现子类方法没有重写的情况,加@Override注解让编译器检查重写是否失败
注意 getXXX 和 getDeclaredXXX 方法的区别,其中 XXX 包括 Methods、Fields、Constructors、Annotations。 //这两类方法,针对不同的成员类型 XXX 和对象,在实现上都有一些细节差异,详情请查看官方文档:https://docs.oracle.com/javase/8/docs/api/java/lang/Class.html //getDeclaredMethods 方法无法获得父类定义的方法,而 getMethods 方法可以,只是差异之一,不能适用于所有的 XXX。
泛型因类型擦除会导致泛型方法T占位符被替换为Object
//子类如果使用具体类型覆盖父类实现,编译器会生成桥接方法。 //这样既满足子类方法重写父类方法的定义,又满足子类实现的方法有具体的类型。 //使用反射来获取方法清单时,需要注意过滤桥接方法
自定义注解可以通过标记元注解@Inherited实现注解的继承,不过这只适用于类
如果要继承定义在接口或方法上的注解,可以使用 Spring 的工具类 AnnotatedElementUtils,并注意各种 getXXX 方法和 findXXX 方法的区别,详情查看Spring 的文档。
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/core/annotation/AnnotatedElementUtils.html
编译后的代码和原始代码并不完全一致
//编译器可能会做一些优化,加上还有诸如 AspectJ 等编译时增强框架, //使用反射动态获取类型的元数据可能会和我们编写的源码有差异,这点需要特别注意。 //因此,可以在反射中多写断言,遇到非预期的情况直接抛异常,避免通过反射实现的业务逻辑不符合预期。