Java: resolución del método en tiempo de ejecución

Estoy trabajando en una invocación dinámica de código a través de un intérprete, y me estoy metiendo en las áreas feas y pegajosas de la resolución de métodos como se explica en Sección 15.12 de JLS.

La forma "fácil" de elegir un método es cuando conoce los tipos exactos de todos los argumentos, momento en el que puede utilizar Class.getDeclaredMethod(String name, Class[] parameterTypes). Tal vez tenga que verificar la accesibilidad del método y las superclases / superinterfaces de la clase.

Pero esto no cubre ninguno de los siguientes casos, por lo que es algo inútil:

  • primitivas de boxeo / unboxing
  • subtipos
  • varargs
  • un argumento nulo (que puede ser de cualquier tipo, a menos que el intérprete sepa lo contrario; en el momento de la compilación, se eliminaría cualquier ambigüedad al lanzar un valor nulo a una clase / interfaz)
  • conversión de tipos primitivos (no forma parte de Java, pero se permite en el contexto de los lenguajes, por ejemplo, Rhino Javascript, donde todos los números son de punto flotante, por lo que el código Java puede tomar un int pero la persona que llama pasa un número que es un int o double)

(vea a continuación un ejemplo rápido de los tres primeros)

Así que ahora tengo que escribir mi propia biblioteca de resolución de métodos ...

¿Existe alguna biblioteca marco conocida para ayudar en esto?

package com.example.test.reflect;

import java.lang.reflect.Method;

public class MethodResolutionTest {
    public void compute(int i)              { /* implementation... */ }
    public void compute(Long l)             { /* implementation... */ }
    public void compute(Object obj)         { /* implementation... */ }
    public void compute(String... strings)  { /* implementation... */ }

    public static void main(String[] args) {
        Class<?> cl = MethodResolutionTest.class;

        /* these succeed */
        findAndPrintMethod(cl, "compute", int.class);
        findAndPrintMethod(cl, "compute", Long.class);
        findAndPrintMethod(cl, "compute", Object.class);
        findAndPrintMethod(cl, "compute", String[].class);

        /* these fail */
        findAndPrintMethod(cl, "compute", Integer.class);
        findAndPrintMethod(cl, "compute", long.class);
        findAndPrintMethod(cl, "compute", MethodResolutionTest.class);
        findAndPrintMethod(cl, "compute", String.class, String.class);
    }
    private static void findAndPrintMethod(Class<?> objectClass, 
            String methodName, Class<?>... parameterTypes) 
    {
        try {
            Method method = findMethod(objectClass, methodName, 
                   parameterTypes);
            System.out.println(method.toString());
        }
        catch (SecurityException e) {
            e.printStackTrace();
        }
        catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }
    private static Method findMethod(Class<?> objectClass, 
            String methodName, Class<?>[] parameterTypes) 
        throws SecurityException, NoSuchMethodException 
    {
        return objectClass.getDeclaredMethod(methodName, parameterTypes);
    }
}

preguntado el 16 de mayo de 11 a las 17:05

@Jonathon: Sé sobre isAssignableFrom. Conseguí algo de cosecha propia trabajando con subtipos y boxeo / unboxing. Luego comencé con los varargs y se puso desagradable, y pensé: "Espera un minuto, ¿por qué estoy haciendo esto?" así que estoy buscando específicamente una biblioteca preexistente. (O al menos un buen conjunto de casos de prueba preexistentes) De lo contrario, puedo hacer esto por mi cuenta, pero es un gran dolor. -

A menos que encuentres una biblioteca agradable, creo que todo lo que hagas será feo y difícil de leer. -

Si lo se. ¿Cómo se puede atraer a los semidioses de Java en Google u Oracle o IBM o Apache o lo que sea para hacer una buena biblioteca de código abierto no viral para hacer esto? Es un fragmento faltante del reflejo de Java, en mi humilde opinión. Puedo encontrar algo que funcione para mis propios propósitos, pero creo que estaría lejos de ser perfecto, ya sea en corrección / documentación / mantenibilidad. -

4 Respuestas

Puede que quieras leer esta entrada del blog y echa un vistazo a Compañera de clases. Debería hacer la mayor parte del trabajo sucio por ti.

contestado el 17 de mayo de 11 a las 13:05

ARGH! Muy prometedor, pero es ligeramente diferente: resuelve tipos genéricos, pero no hace nada por la resolución de métodos. Pero al menos es un comienzo, y apuesto a que podría engatusar a los autores para que lo amplíen. - Jason S

Los beanutils comunes tienen esta función para encontrar métodos coincidentes: getMatchingAccessibleMethod

Funciona con tipos primitivos pero es algo indeterminista como dice la documentación.

contestado el 17 de mayo de 11 a las 02:05

Tenga en cuenta que en getMatchingAccessibleMethod la coincidencia primitiva es unidireccional - parámetro Long.class coincidirá foo(long) , pero Long.TYPE no coincidirá foo(Long) - McDowell

+1: @Bengt: qué alivio descubrir que Apache tiene algunas herramientas, tal vez no lo que yo quería, pero al menos algo. - Jason S

Uno de los usuarios de guava-discus me recomendó que mire el Biblioteca espejo.

Desafortunadamente, tampoco maneja la resolución de sobrecarga, al menos no en la actualidad.

contestado el 20 de mayo de 11 a las 20:05

He tenido cierto éxito con MVEL y las propiedades, pero no recuerdo si puede enviar llamadas a métodos.

Sin embargo, es posible que esté buscando un idioma completamente diferente. Dada la naturaleza del problema, sugiero echar un vistazo a Groovy, que se integra muy limpiamente con Java.

contestado el 17 de mayo de 11 a las 02:05

hmm, estoy "atascado" con Java por varias razones, la más importante es la capacidad de mantenimiento (agregar un lenguaje diferente limita significativamente mi capacidad para obtener ayuda de otros desarrolladores) - Jason S

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.