Java 反射之获取方法对象

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zhangxin09/article/details/78941797

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

猜你喜欢

转载自blog.csdn.net/zhangxin09/article/details/78941797