Pregunta

Tengo una clase que utiliza XML y reflexión para volver Objects a otra clase.

Normalmente estos objetos son subcampos de un objeto externo, pero en ocasiones es algo que quiero generar sobre la marcha. He intentado algo como esto, pero en vano. Creo que es porque Java no le permitirá acceder a los 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 es proporcionado private, se produce un error con un NoSuchMethodException. Podría resolverlo haciendo que el método public, o hacer otra clase para derivarla de.

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

¿Fue útil?

Solución

Puede invocar método privado con la reflexión. Modificar 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. En primer lugar, se getDeclaredMethod sólo encuentran método declarado en el Class actual, no heredadas de supertipos. Por lo tanto, recorrer la jerarquía de clases concreta si es necesario. En segundo lugar, un SecurityManager puede evitar el uso del método setAccessible. Por lo tanto, es posible que tenga que ejecutar como PrivilegedAction (usando AccessController o Subject).

Otros consejos

Utilice getDeclaredMethod() para obtener un objeto Método privado y luego utilizar method.setAccessible() para permitir que en realidad llamarlo.

Si el método acepta tipo de datos no primitivos entonces el siguiente método se puede utilizar 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;
    }

El parámetro aceptado son obj, methodName y los parámetros. Por ejemplo

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

Método concatString puede invocarse como

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

Usted puede hacer esto utilizando ReflectionTestUtils de la primavera ( org.springframework.test.util.ReflectionTestUtils )

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

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

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

Permítanme ofrecer código completo de ejecución métodos protegidos a través de la reflexión. Es compatible con cualquier tipo de params incluyendo genéricos, params autoboxed y valores nulos

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

método utiliza ClassUtils Apache para verificar la compatibilidad de params autoboxed

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

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

Permite modificar cualquier campos como constantes estáticas finales y llamar a métodos protegidas ino sin especificar clase concreta en la jerarquía se herencia

<!-- 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>

Puede utilizar del colector @Jailbreak para el directo, el tipo -caja fuerte reflexión de Java:

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

public class Foo {
    private void callMe();
}

@Jailbreak desbloquea la variable local foo en el compilador para el acceso directo a todos los miembros de la jerarquía de Foo.

Del mismo modo se puede utilizar el jailbreak () método de extensión para el uso de una sola vez:

foo.jailbreak().callMe();

A través del método jailbreak() puede acceder a cualquier miembro de la jerarquía de Foo.

En ambos casos el compilador resuelve la llamada al método para escribir, con seguridad, como si un método público, mientras Múltiple genera código reflexión eficiente para usted bajo el capó.

Por otra parte, si el tipo no se conoce de forma estática, puede utilizar estructural Typing para definir una interfaz de un tipo puede satisfacer sin tener que declarar su aplicación. Esta estrategia mantiene seguridad de tipos y evita la identidad y las prestaciones problemas asociados con el código de la reflexión y el proxy.

Para descubrir más sobre colector .

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top