GetMethod 调出方法
已知一个类,可以将其身上的方法抽出来。抽出来的便是一个 Java 对象,也就说,Java 中方法也是一种对象,为 java.lang.reflect.Method 类型。我们通过反射调出、执行方法。
Java API 为我们提供了两种获取方法的方法,getDeclaredMethod 和 getMethod。区别在于前者或是本类身上的方法,不会获取父类身上的方法,而后者却可以做到,不过 getDeclaredMethod 能够获取 private 方法,这点事 getMethod 所做不到的。AJAXJS-Base 认为获取 private 方法的场合很少,故封装的时候便没考虑使用 getDeclaredMethod 而是采用 getMethod。
GetMethod.getMethod() 用法如下:
import static com.ajaxjs.util.reflect.GetMethod.*;
class Foo {
public void m1() {
}
public void m1(String arg) {
}
}
class Bar extends Foo {
public void m2() {
}
}
@Test
public void testGetMethod() {
assertNotNull(getMethod(new Foo(), "m1"));// 按实际对象
assertNotNull(getMethod(Foo.class, "m1"));// 按类引用
assertNotNull(getMethod(Foo.class, "m1", String.class)); // 按参数类型
assertNotNull(getMethod(Foo.class, "m1", "foo"));// 按实际参数
assertNotNull(getMethod(Bar.class, "m1"));
assertNotNull(getMethod(Bar.class, "m1", String.class));
assertNotNull(getMethod(Bar.class, "m2"));
}
无论是类引用还是实例,都是可以传入实际参数或参数其类对象。但是传实际参数对象会有一个问题:试想想看,我们的 Java 调用方法的时候,是允许传入参数的子类对象的,也就是说,A 是 B 的父类,同时 foo(A) 声明了方法,那么 foo(B)传入 B 类对象的参数是没问题的,——这就是所谓的“向上转型(UpCasting)”。遗憾的是,直接调用 getMethod(Foo1.class, “foo”, new Bar2()) 是找不到的,如下所示。
class Foo1 {
public void foo(Foo1 a) {
}
}
class Bar2 extends Foo1 {
}
assertTrue(getMethod(Foo1.class, "foo", new Bar2()) == null); // 找不到
对此,可以考虑如下:
- 手动强类型转换,不过在框架编码中很难手动手动指定
- 使用 getMethod(Object obj, String methodName, Class.. args) 指定类型,和上个方法同理,在框架也是很难手动指定。
- 使用 getMethodByUpCastingSearch() 自动转换
getMethodByUpCastingSearch() 自动转换
自动循环参数类型向上转型。仅支持一个参数。
public static Method getMethodByUpCastingSearch(Class<?> cls, String methodName, Object arg);
当前该方法没有提供参数类对象的方法。
接口类型自动转换
上面谈到的是父类、子类之间的类型匹配问题的。那么接口类型呢?接口的话处理起来麻烦多,但还是可以自动分析转换的。参见下面例子:
public static class A {
public String foo(A a) {
return "A.foo";
}
public String bar(C c) {
return "A.bar";
}
}
public static class B extends A {
}
public static interface C {
}
public static class D implements C {
}
@Test
public void testDeclaredMethod() {
assertTrue(getMethodByUpCastingSearch(A.class, "bar", new D()) == null); // 找不到
assertNotNull(getDeclaredMethodByInterface(A.class, "bar", new D()));// 找到了
}
getDeclaredMethodByInterface() 内部工序是:先变量参数类型,通过 Type[] intfs = clazz.getGenericInterfaces() 方法返回类所有的接口类型,然后遍历之,如果发现接口有继承关系,还要进行遍历。所以代码内部经历三次 for 循环才可以找到匹配的方法。
忽略参数类型 getSuperClassDeclaredMethod()
查找对象父类身上指定的方法(注意该方法不需要校验参数类型是否匹配,故有可能不是目标方法,而造成异常,请谨慎使用)。
相关类的源码:https://gitee.com/sp42/ajaxjs-base/raw/master/src/main/com/ajaxjs/util/reflect/GetMethod.java