Un modo per richiamare un metodo privato?

Ho una class che usa XML e reflection per restituire Object s ad un’altra class.

Normalmente questi oggetti sono campi secondari di un object esterno, ma occasionalmente è qualcosa che voglio generare al volo. Ho provato qualcosa di simile ma senza successo. Credo che sia perché Java non ti permetterà di accedere a metodi private per la riflessione.

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

Se il metodo fornito è private , fallisce con una NoSuchMethodException . Potrei risolverlo rendendo public il metodo, o facendo in modo che un’altra class la ricava.

Per farla breve, mi stavo chiedendo se c’era un modo per accedere a un metodo private attraverso la riflessione.

Puoi invocare il metodo privato con la riflessione. Modifica dell’ultimo bit del codice postato:

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

Ci sono un paio di avvertimenti. In primo luogo, getDeclaredMethod troverà solo il metodo dichiarato nella Class corrente, non ereditato dai supertipi. Quindi, attraversare la gerarchia di class concreta se necessario. In secondo luogo, un SecurityManager può impedire l’uso del metodo setAccessible . Quindi, potrebbe essere necessario eseguire come PrivilegedAction (usando AccessController o Subject ).

Utilizzare getDeclaredMethod() per ottenere un object Method privato e quindi utilizzare method.setAccessible() per consentire di chiamarlo effettivamente.

Se il metodo accetta il tipo di dati non primitivo, è ansible utilizzare il seguente metodo per richiamare un metodo privato di qualsiasi class:

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

I parametri accettati sono obj, methodName e i parametri. Per esempio

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

Il metodo concatString può essere invocato come

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

Consentitemi di fornire un codice completo per i metodi protetti dall’esecuzione tramite riflessione. Supporta qualsiasi tipo di param compresi generici, param autoboxed e valori nulli

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

Il metodo utilizza ClassUtils di apache per verificare la compatibilità dei parametri autoboxed

puoi farlo usando ReflectionTestUtils of Spring ( org.springframework.test.util.ReflectionTestUtils )

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

Esempio: se hai una class con un metodo square(int x) privato square(int x)

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

Un’altra variante utilizza la libreria JOOR molto potente https://github.com/jOOQ/jOOR

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

Permette di modificare qualsiasi campo come costanti statiche finali e chiamare i metodi protetti senza specificare la class concreta nella gerarchia dell’ereditarietà

   org.jooq joor-java-8 0.9.7