2011-05-16 18 views
15

Estoy trabajando en una invocación dinámica de código a través de un intérprete, y estoy entrando en las áreas desagradables y pegajosas de la resolución de métodos como se describe en JLS section 15.12.Java: resolución de método de tiempo de ejecución

La manera "fácil" de elegir un método es cuando conoce los tipos exactos de todos los argumentos, en cuyo punto puede usar 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 cualquiera de los siguientes casos, por lo que es algo inútil:

  • boxeo/primitivas unboxing
  • subtipos
  • varargs
  • un argumento nulo (que puede ser cualquier tipo, a menos que el intérprete sepa lo contrario; en tiempo de compilación, cualquier ambigüedad sería eliminar al convertir nulo a una clase/interfaz)
  • conversión de tipo primitivo (no es parte de Java, pero está permitida en contexto de idiomas - p. Rhino Javascript donde todos los números son de punto flotante, por lo que el código Java podría tomar una int pero la persona que llama que pasa en un número que es o bien un int o una double)

(ver más abajo para un ejemplo rápido de la primera tres)

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

¿hay alguna biblioteca marco conocido 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); 
    } 
} 
+1

[Esta respuesta maneja algunos de sus casos como subtipos] (http://stackoverflow.com/questions/2580665/java-getmethod-with-superclass-parameters-in-method/2580699#2580699). –

+1

@Jonathon: sé de isAssignableFrom. Obtuve algo de cosecha propia trabajando con subtipos y boxeo/unboxing. Luego comencé a varargs y se puso desagradable, y pensé, "Espera un minuto, ¿por qué estoy haciendo esto?" entonces 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. –

+1

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

Respuesta

-1

que he tenido algo de éxito con MVEL y propiedades, pero no puedo recordar si se puede enviar llamadas a métodos.

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

+0

hmm, estoy "atascado" con Java por varias razones, la más grande es la capacidad de mantenimiento (agregar un idioma diferente limita significativamente mi capacidad de obtener ayuda de otros desarrolladores) –

2

Commons BeanUtils tienen esta función para encontrar métodos de emparejamiento: getMatchingAccessibleMethod

Se trabaja con tipos primitivos pero es un poco indeterminista como dice el doc.

+1

Tenga en cuenta que en 'getMatchingAccessibleMethod' la coincidencia primitiva es de una sola manera: parámetro 'Long.class' coincidirá' foo (long) 'pero' Long.TYPE' no coincidirá con 'foo (Long)' – McDowell

+0

+1: @Bengt: qué alivio saber Apache tiene algunas herramientas, tal vez no lo que quería, pero algo al menos. –

3

Es posible que desee leer this blog post y consultar ClassMate. Debería hacer la mayor parte del trabajo sucio para ti.

+0

ARGH! Es muy prometedor, pero es ligeramente diferente: resuelve tipos genéricos, pero no hace nada para 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. –

0

Uno de los usuarios de guava-discuss recomendado, miro el Mirror library.

Desafortunadamente, tampoco resuelve la resolución de sobrecarga, al menos no en este momento.

Cuestiones relacionadas