2010-02-09 20 views
5

Como parte del desarrollo de una pequeña ScriptEngine, que reflexivamente llamar a métodos Java. Una llamada del motor de script me proporciona el objeto, el nombre del método y una matriz de argumentos. Para llamar al método, traté de resolverlo con una llamada a Class.getMethod (nombre, tipos de argumentos).
Sin embargo, esto solo funciona cuando las clases de los argumentos y las clases esperadas por el Método son las mismas.Calling más cercano método de ajuste

Object o1 = new Object(); 
Object out = System.out; 
//Works as System.out.println(Object) is defined 
Method ms = out.getClass().getMethod("println",o1.getClass()); 
Object o2 = new Integer(4); 
//Does not work as System.out.println(Integer) is not defined 
Method mo = out.getClass().getMethod("println",o2.getClass()); 

me gustaría saber si hay una manera "simple" para obtener el método correcto, si es posible con el ajuste más cercano de los tipos de argumentos, o si tengo que aplicar esto a mí mismo.

ajuste más cercano sería:

Object o1 = new Integer(1); 
Object o2 = new String(""); 
getMethod(name, o1.getClass())//println(Object) 
getMethod(name, o2.getClass())//println(String) 

Actualización:
Para aclarar lo que necesito: el motor de guiones es un pequeño proyecto que escribo en mi tiempo libre así que no hay reglas STRIKT que tengo seguir. Así que pensé que la selección de los métodos llamó desde el motor de la misma manera que el compilador Java selecciona métodos en tiempo de compilación solamente con el tipo dinámico y no el tipo estático del objeto funcionaría. (Con o sin autoboxing)
Esto es lo que primero esperaba que Class.getMethod() resolviera. Pero el Class.getMethod() requiere las mismas clases exactas que los tipos de argumento como declara el método, el uso de una subclase dará como resultado un método que no Exception. Esto puede suceder por buenas razones, pero hace que el método sea inútil para mí, ya que no sé de antemano qué tipos de argumento cabrían.
Una alternativa sería llamar Class.getMethods() y iterar a través de la matriz devuelta y tratar de encontrar un método apropiado. Sin embargo, esto sería complicado si yo no sólo quiero dar el primer método de "bueno" el que me encuentro, por lo que espera que no habría una solución existente cuales al menos manijas:

  • ajuste más cercano: si arg.getClass() == de subclases y métodos m (superclase), m (subclase) entonces llaman m (subclase)
  • argumentos variables: System.out.printf (String, String ...)

El soporte para el autoboxing también sería bueno.
Si una llamada no se puede resolver, puede emitir una excepción (ma (String, Object), ma (Object, String), args = String, String)
(Si ha llegado hasta aquí, gracias por tomarse el tiempo para leyó :-))

+0

primero definir reglas estrictas para "más cercano", entonces la aplicación es un detalle. – Bozho

+0

justo lo que me preguntaba, también. Pero para todos los contestadores pedantemente correctos que dicen: "Definir el más cercano", tengo uno fácil: el que Java normalmente elegiría (supongo que en la carga de clases). –

Respuesta

2

Como han señalado otros, no existe un método estándar que lo haga, por lo que tendrá que implementar su propio algoritmo de resolución de sobrecarga.

probablemente tendría sentido seguir las reglas de resolución de sobrecarga de javac lo más cerca posible:
http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#292575
Probablemente puede ignorar los genéricos para un lenguaje de programación de tipo dinámico, pero todavía podría beneficiarse de la bridge methods que el compilador genera automáticamente.

Algunos escollos a tener en cuenta:

  • Class.isAssignableFrom no sabe acerca de las conversiones primitivos ampliación automáticas, ya que estos son azúcar sintáctica implementado en el compilador; No ocurren en la VM o la jerarquía de clases. p.ej. int.class.isAssignableFrom(short.class) devuelve false.
  • Similarmente, Class.isAssignableFrom no sabe acerca de auto-boxing. Integer.class.isAssignableFrom(int.class) devuelve false.
  • Class.isInstance y Class.cast tome un Object como argumento; No puede pasarles valores primitivos. También devuelven un Object, por lo que no se pueden utilizar para unboxing ((int) new Integer(42) es legal en fuente de Java, pero int.class.cast(new Integer(42)) se produce una excepción.)
1

yo sepa, no existe una forma sencilla de hacer este tipo de cosas. Ciertamente, no hay nada en las bibliotecas de clases estándar de Java para hacer esto.

El problema es que no hay una única respuesta "correcta". Debe considerar todos sus casos de uso, decidir cuál debería ser el "método correcto" e implementar su código de reflexión en consecuencia.

2

Yo sugeriría que utilice getMethods(). Devuelve una matriz de todos los métodos públicos (Method[]).

Lo más importante aquí es:
" Si la clase declara múltiples métodos miembro públicas con los mismos tipos de parámetros, todos ellos están incluidos en la matriz devuelta. "

Lo que a continuación, tendrá que hacer es usar los resultados en esta matriz para determinar los cuales uno de ellos (si los hay) son la medida más próxima. Dado que la coincidencia más cercana depende en gran medida de sus requisitos y de su aplicación específica, tiene sentido codificarla usted mismo.


Código de ejemplo que ilustra un enfoque de cómo puede ir haciendo esto:

public Method getMethod(String methodName, Class<?> clasz) 
{ 
    try 
    { 
     Method[] methods = clasz.getMethods(); 
     for (Method method : methods) 
     { 
      if (methodName.equals(method.getName())) 
      { 
       Class<?>[] params = method.getParameterTypes(); 
       if (params.length == 1) 
       { 
        Class<?> param = params[0]; 
        if ((param == int.class) || (param == float.class) || (param == float.class)) 
        { 
         //method.invoke(object, value); 
         return method; 
        } 
        else if (param.isAssignableFrom(Number.class)) 
        { 
         return method; 
        } 
        //else if (...) 
        //{ 
        // ... 
        //} 
       } 
      } 
     } 
    } 
    catch (Exception e) 
    { 
     //some handling 
    } 
    return null; 
} 

En este ejemplo, el método getMethod(String, Class<?>) devolverá un método que con un solo parámetro que es un int, float , double, o una superclase de Number.

Esta es una implementación rudimentaria: devuelve el primer método que se ajusta a la ley. Debería ampliarlo para crear una lista de todos los métodos que coinciden, y luego ordenarlos de acuerdo con algún tipo de criterio, y devolver el mejor método de coincidencia.

A continuación, puede llevarlo aún más lejos al crear el método más general getMethod(String, Class<?>), para manejar más de los escenarios posibles "estrecha coincidencia", y posiblemente incluso más de un parametro

HTH


Editar: Como @finnw ha señalado, tenga cuidado al usar Class#isAssignableFrom(Class<?> cls), debido a sus limitaciones, como lo he hecho en mi código de muestra, probando las primitivas por separado de los objetos Number.

+1

@bguiz, buena solución. Solo quiero agregar que puede probar 'Class.isPrimitive()' y 'Class.isArray()' para evitar algunas de las limitaciones de isAssignableFrom(). La respuesta de @ finnw también. – bojangle