Domanda

Ho una classe che utilizza XML e di riflessione per tornare Objects ad un'altra classe.

Di solito questi oggetti sono sub campi di un oggetto esterno, ma a volte si tratta di qualcosa che voglio generare al volo. Ho provato qualcosa di simile, ma senza alcun risultato. Credo che sia perché Java non vi permetterà di accedere a metodi private di 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, non riesce con un NoSuchMethodException. Potrei risolverlo facendo il metodo public, o fare un'altra classe per derivare da esso.

Per farla breve, mi stavo chiedendo se ci fosse un modo per accedere a un metodo di private attraverso la riflessione.

È stato utile?

Soluzione

È possibile richiamare il metodo privato con la riflessione. Modificare l'ultimo bit del codice scritto:

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

Ci sono un paio di avvertimenti. In primo luogo, getDeclaredMethod troverete solo metodo dichiarato nella Class corrente, non ereditato da supertipi. Quindi, attraversare la gerarchia delle classi di cemento, se necessario. In secondo luogo, un SecurityManager può impedire l'uso del metodo setAccessible. Quindi, potrebbe essere necessario per l'esecuzione come un PrivilegedAction (utilizzando AccessController o Subject).

Altri suggerimenti

Utilizza getDeclaredMethod() per ottenere un oggetto metodo privato e quindi utilizzare method.setAccessible() per consentire di chiamare in realtà.

Se il metodo accetta tipo di dati non primitivi quindi il seguente metodo può essere utilizzato per invocare un metodo privato di qualsiasi classe:

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

Il Parametro accettate sono obj, methodName ei parametri. Ad esempio

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

Metodo concatString può essere invocato come

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

È possibile farlo utilizzando ReflectionTestUtils di Primavera ( org.springframework.test.util.ReflectionTestUtils )

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

Esempio: se si dispone di una classe con un metodo square(int x) privato

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

Mi permetta di fornire il codice completo per metodi protetti di esecuzione attraverso la riflessione. Supporta tutti i tipi di params tra farmaci generici, params autoboxed e valori nulli

@SuppressWarnings("unchecked")
public static <T> T executeSuperMethod(Object instance, String methodName, Object... params) throws Exception {
    return executeMethod(instance.getClass().getSuperclass(), instance, methodName, params);
}

public static <T> T executeMethod(Object instance, String methodName, Object... params) throws Exception {
    return executeMethod(instance.getClass(), instance, methodName, params);
}

@SuppressWarnings("unchecked")
public static <T> 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);
}

Metodo utilizza ClassUtils apache per verificare la compatibilità di params autoboxed

Ancora una variante sta usando molto potente Joor biblioteca https://github.com/jOOQ/jOOR

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

Permette di modificare i campi come costanti statiche finali e chiamare i metodi protetti ino senza specificare classe concreta nel hierarhy eredità

<!-- https://mvnrepository.com/artifact/org.jooq/joor-java-8 -->
<dependency>
     <groupId>org.jooq</groupId>
     <artifactId>joor-java-8</artifactId>
     <version>0.9.7</version>
</dependency>

È possibile utilizzare del Collettore @Jailbreak per la diretta, tipo -safe riflessione Java:

@Jailbreak Foo foo = new Foo();
foo.callMe();

public class Foo {
    private void callMe();
}

@Jailbreak sblocca il foo variabile locale nel compilatore per l'accesso diretto a tutti i membri della gerarchia di Foo.

Allo stesso modo è possibile utilizzare il jailbreak () metodo di estensione per una tantum uso:

foo.jailbreak().callMe();

Attraverso il metodo jailbreak() è possibile accedere a qualsiasi membro della gerarchia di Foo.

In entrambi i casi il compilatore risolve la chiamata al metodo per la digitazione, in modo sicuro, come se un metodo pubblico, mentre collettore genera codice riflessione efficiente per voi sotto il cofano.

In alternativa, se il tipo non è noto staticamente, è possibile utilizzare strutturale Typing definire un'interfaccia di tipo può soddisfare senza dover dichiarare la sua attuazione. Questa strategia mantiene tipo di sicurezza ed evita problemi di prestazioni e di identità associati con la riflessione e delega codice.

Scopri di più su Collettore .

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top