Java利用反射实现运行时方法调用

1.介绍

在这篇短文中,我们将快速了解如何在运行时使用Java反射API调用方法。

2.准备工作

来创建一个简单的类:

public class Operations {
    
    
    public double publicSum(int a, double b) {
    
    
        return a + b;
    }

    public static double publicStaticMultiply(float a, long b) {
    
    
        return a * b;
    }

    private boolean privateAnd(boolean a, boolean b) {
    
    
        return a && b;
    }

    protected int protectedMax(int a, int b) {
    
    
        return a > b ? a : b;
    }
}

3.获取一个方法对象

首先,我们需要得到一个Method对象来反射我们想要调用的方法。Class对象表示在其中定义方法的类型,它提供了两种方法。

3.1 getMethod()

我们可以使用getMethod()查找任何公共方法,无论是静态方法还是在类或其任何超类中定义的实例。

它接收方法名称作为第一个参数,后跟方法参数的类型:

Method sumInstanceMethod
  = Operations.class.getMethod("publicSum", int.class, double.class);

Method multiplyStaticMethod
  = Operations.class.getMethod(
    "publicStaticMultiply", float.class, long.class);

3.2 getDeclaredMethod()

我们可以使用getDeclaredMethod()获取类中定义的任何方法。这包括public、protected、default访问,甚至私有方法,但不包括继承的方法。

它接收与getMethod()相同的参数:

Method andPrivateMethod
  = Operations.class.getDeclaredMethod(
    "privateAnd", boolean.class, boolean.class);
Method maxProtectedMethod
  = Operations.class.getDeclaredMethod("protectedMax", int.class, int.class);

4.调用方法

有了方法实例,我们现在可以调用invoke()来执行其潜在方法并获取返回的对象。

4.1 调用实例方法

要调用实例方法,invoke()的第一个参数必须是反射所调用方法的方法实例:

@Test
public void givenObject_whenInvokePublicMethod_thenCorrect() {
    
    
    Method sumInstanceMethod
      = Operations.class.getMethod("publicSum", int.class, double.class);

    Operations operationsInstance = new Operations();
    Double result
      = (Double) sumInstanceMethod.invoke(operationsInstance, 1, 3);

    assertThat(result, equalTo(4.0));
}

4.2 调用静态方法

因为静态方法不需要调用实例,所以我们可以传递null作为第一个参数:

@Test
public void givenObject_whenInvokeStaticMethod_thenCorrect() {
    
    
    Method multiplyStaticMethod
      = Operations.class.getDeclaredMethod(
        "publicStaticMultiply", float.class, long.class);

    Double result
      = (Double) multiplyStaticMethod.invoke(null, 3.5f, 2);

    assertThat(result, equalTo(7.0));
}

5.方法的可访问性

默认情况下,并非所有反射的方法都是可访问的。这意味着JVM在调用它们时强制执行访问控制检查。

例如,如果我们尝试在其定义类之外调用私有方法,或从子类或其类的包之外调用受保护的方法,我们将得到IllegalAccessException:

@Test(expected = IllegalAccessException.class)
public void givenObject_whenInvokePrivateMethod_thenFail() {
    
    
    Method andPrivateMethod
      = Operations.class.getDeclaredMethod(
        "privateAnd", boolean.class, boolean.class);

    Operations operationsInstance = new Operations();
    Boolean result
      = (Boolean) andPrivateMethod.invoke(operationsInstance, true, false);

    assertFalse(result);
}

@Test(expected = IllegalAccessException.class)
public void givenObject_whenInvokeProtectedMethod_thenFail() {
    
    
    Method maxProtectedMethod
      = Operations.class.getDeclaredMethod(
        "protectedMax", int.class, int.class);

    Operations operationsInstance = new Operations();
    Integer result
      = (Integer) maxProtectedMethod.invoke(operationsInstance, 2, 4);
    
    assertThat(result, equalTo(4));
}

通过对反射方法对象执行setAccessible(true),JVM将取消访问控制检查,并允许我们在不引发异常的情况下调用该方法:

@Test
public void givenObject_whenInvokePrivateMethod_thenCorrect() {
    
    
    // ...
    andPrivateMethod.setAccessible(true);
    // ...
    Boolean result
      = (Boolean) andPrivateMethod.invoke(operationsInstance, true, false);

    assertFalse(result);
}

@Test
public void givenObject_whenInvokeProtectedMethod_thenCorrect() {
    
    
    // ...
    maxProtectedMethod.setAccessible(true);
    // ...
    Integer result
      = (Integer) maxProtectedMethod.invoke(operationsInstance, 2, 4);

    assertThat(result, equalTo(4));
}

猜你喜欢

转载自blog.csdn.net/IndexMan/article/details/118392549