反射、注解和泛型遇到重载和继承时注意事项


反射、注解和泛型遇到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 等编译时增强框架,
//使用反射动态获取类型的元数据可能会和我们编写的源码有差异,这点需要特别注意。
//因此,可以在反射中多写断言,遇到非预期的情况直接抛异常,避免通过反射实现的业务逻辑不符合预期。

相关