Llamar a métodos en tiempo de ejecución mediante Java Reflection

1. Información general

En este breve artículo, veremos rápidamente cómo invocar métodos en tiempo de ejecución utilizando la API de Java Reflection .

2. Preparándose

Creemos una clase simple que usaremos para los ejemplos que siguen:

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. Obtención de un objeto de método

Primero, necesitamos obtener un objeto Método que refleje el método que queremos invocar. El objeto Class , que representa el tipo en el que se define el método, proporciona dos formas de hacerlo.

3.1. getMethod ()

Podemos usar getMethod () para encontrar cualquier método público, ya sea estático o instancia que esté definido en la clase o cualquiera de sus superclases.

Recibe el nombre del método como primer argumento, seguido de los tipos de argumentos del método:

Method sumInstanceMethod = Operations.class.getMethod("publicSum", int.class, double.class); Method multiplyStaticMethod = Operations.class.getMethod( "publicStaticMultiply", float.class, long.class);

3.2. getDeclaredMethod ()

Podemos usar getDeclaredMethod () para obtener cualquier método definido en la clase. Esto incluye métodos públicos, protegidos, de acceso predeterminado e incluso privados, pero excluye los heredados.

Recibe los mismos parámetros que getMethod () :

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

4. Métodos de invocación

Con la instancia del método en su lugar, ahora podemos llamar a invoke () para ejecutar el método subyacente y obtener el objeto devuelto.

4.1. Métodos de instancia

Para invocar un método de instancia, el primer argumento para invocar () debe ser una instancia de Method que refleje el método que se invoca:

@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. Métodos estáticos

Dado que estos métodos no requieren que se llame a una instancia, podemos pasar nulo como primer argumento:

@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. Accesibilidad al método

De forma predeterminada, no se puede acceder a todos los métodos reflejados . Esto significa que la JVM impone las comprobaciones de control de acceso cuando las invoca.

Por ejemplo, si intentamos llamar a un método privado fuera de su clase definitoria o un método protegido desde fuera de una subclase o paquete de su clase, obtendremos una 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)); }

Al llamar a setAccesible (true) en un objeto de método reflejado, la JVM suprime las comprobaciones de control de acceso y nos permite invocar el método sin lanzar una excepción:

@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)); }

6. Conclusión

En este artículo rápido, hemos visto cómo llamar a métodos estáticos y de instancia de una clase en tiempo de ejecución mediante la reflexión. También mostramos cómo cambiar la marca de accesibilidad en los objetos de método reflejados para suprimir las comprobaciones de control de acceso de Java al invocar métodos privados y protegidos.

Como siempre, el código de ejemplo se puede encontrar en Github.