¿Alguna forma de invocar un método privado?

Tengo una clase que usa XML y reflexión para devolver Object s a otra clase.

Normalmente, estos objetos son subcampos de un objeto externo, pero ocasionalmente es algo que quiero generar sobre la marcha. He intentado algo como esto, pero fue en vano. Creo que es porque Java no le permitirá acceder a métodos private para la reflexión.

 Element node = outerNode.item(0); String methodName = node.getAttribute("method"); String objectName = node.getAttribute("object"); if ("SomeObject".equals(objectName)) object = someObject; else object = this; method = object.getClass().getMethod(methodName, (Class[]) null); 

Si el método proporcionado es private , falla con una NoSuchMethodException . Podría resolverlo haciendo public el método o haciendo otra clase para derivarlo.

Para resumir, me preguntaba si había una forma de acceder a un método private través de la reflexión.

Puede invocar el método privado con la reflexión. Modificando el último bit del código publicado:

 Method method = object.getClass().getDeclaredMethod(methodName); method.setAccessible(true); Object r = method.invoke(object); 

Hay un par de advertencias. Primero, getDeclaredMethod solo encontrará el método declarado en la Class actual, no heredado de supertipos. Por lo tanto, recorra la jerarquía de clases concretas si es necesario. En segundo lugar, un SecurityManager puede evitar el uso del método setAccessible . Por lo tanto, es posible que deba ejecutarse como una PrivilegedAction (utilizando AccessController o Subject ).

Use getDeclaredMethod() para obtener un objeto Method privado y luego use method.setAccessible() para permitir que realmente lo llame.

Si el método acepta un tipo de datos no primitivo, se puede usar el siguiente método para invocar un método privado de cualquier clase:

 public static Object genericInvokeMethod(Object obj, String methodName, Object... params) { int paramCount = params.length; Method method; Object requiredObj = null; Class[] classArray = new Class[paramCount]; for (int i = 0; i < paramCount; i++) { classArray[i] = params[i].getClass(); } try { method = obj.getClass().getDeclaredMethod(methodName, classArray); method.setAccessible(true); requiredObj = method.invoke(obj, params); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } return requiredObj; } 

Los parámetros aceptados son obj, methodName y los parámetros. Por ejemplo

 public class Test { private String concatString(String a, String b) { return (a+b); } } 

El método concatString se puede invocar como

 Test t = new Test(); String str = (String) genericInvokeMethod(t, "concatString", "Hello", "Mr.x"); 

Permítanme proporcionar un código completo para los métodos de ejecución protegida a través de la reflexión. Es compatible con cualquier tipo de parámetros, incluidos generics, params autoboxados y valores nulos

 @SuppressWarnings("unchecked") public static  T executeSuperMethod(Object instance, String methodName, Object... params) throws Exception { return executeMethod(instance.getClass().getSuperclass(), instance, methodName, params); } public static  T executeMethod(Object instance, String methodName, Object... params) throws Exception { return executeMethod(instance.getClass(), instance, methodName, params); } @SuppressWarnings("unchecked") public static  T executeMethod(Class clazz, Object instance, String methodName, Object... params) throws Exception { Method[] allMethods = clazz.getDeclaredMethods(); if (allMethods != null && allMethods.length > 0) { Class[] paramClasses = Arrays.stream(params).map(p -> p != null ? p.getClass() : null).toArray(Class[]::new); for (Method method : allMethods) { String currentMethodName = method.getName(); if (!currentMethodName.equals(methodName)) { continue; } Type[] pTypes = method.getParameterTypes(); if (pTypes.length == paramClasses.length) { boolean goodMethod = true; int i = 0; for (Type pType : pTypes) { if (!ClassUtils.isAssignable(paramClasses[i++], (Class) pType)) { goodMethod = false; break; } } if (goodMethod) { method.setAccessible(true); return (T) method.invoke(instance, params); } } } throw new MethodNotFoundException("There are no methods found with name " + methodName + " and params " + Arrays.toString(paramClasses)); } throw new MethodNotFoundException("There are no methods found with name " + methodName); } 

Method utiliza apache ClassUtils para verificar la compatibilidad de los params autoboxados

puedes hacer esto usando ReflectionTestUtils of Spring ( org.springframework.test.util.ReflectionTestUtils )

 ReflectionTestUtils.invokeMethod(instantiatedObject,"methodName",argument); 

Ejemplo: si tienes una clase con un método privado square(int x)

 Calculator calculator = new Calculator(); ReflectionTestUtils.invokeMethod(calculator,"square",10); 

Una variante más es el uso de la biblioteca JOOR muy poderosa https://github.com/jOOQ/jOOR

 MyObject myObject = new MyObject() on(myObject).get("privateField"); 

Permite modificar cualquier campo como las constantes estáticas finales y llamar a los métodos protegidos yne sin especificar una clase concreta en la jerarquía de herencia

   org.jooq joor-java-8 0.9.7