2010-12-01 29 views
10

El problema: me gustaría ser capaz de acceder de forma genérica en Java cualquier propiedad/campo en una de Java ojbect de manera similar a cómo un lenguaje dinámico (creo maravilloso, JavaScript) sería. No sabré en el momento en que estoy escribiendo este código de plomería qué tipo de objeto es o cuál será el nombre de propiedad/campo. Pero sabré el nombre de la propiedad/campo cuando vaya a usarlo.Simulación de la tipificación de pato en Java

Mi solución actual: Hasta ahora he escrito una clase de contenedor simple que utiliza java.beans.Introspector para agarrar las propiedades de un Bean/POJO y exponerlos como Map<String, Object>. Es crudo, pero funciona para casos simples.

Mi pregunta ¿Qué otras metodologías hay para abordar este problema además de la reflexión/conversión a un Mapa?

Antes de ir mucho más lejos en este camino, me gustaría saber si alguien sabe cómo podría extraer algo de Rhino o quizás javax.script.* que tiene una implementación bien pensada de este concepto. O tal vez un enfoque completamente diferente que no he considerado.

Edit: sí Estoy familiarizado con la reflexión (que creo que es lo que Introspector está usando debajo del capó de todos modos). Solo tenía curiosidad si había otras soluciones bien pensadas.

Editar 2: Parece que las respuestas más populares implican 1) reflexión directamente o a través de clases de ayuda, y/o 2) asignación a las interfaces que implementan los miembros de clase deseados. Estoy realmente intrigado por el comentario que habla sobre el aprovechamiento de Groovy. Dado que Groovy tiene un verdadero tipado de pato y es un lenguaje JVM, ¿hay alguna manera de hacer un simple ayudante en Groovy y llamarlo desde Java? Esto sería genial y probablemente más flexible y tenga un mejor rendimiento.

Respondo: Marqué la respuesta de Mike como la mejor ya que es un concepto completo que se acerca más. Probablemente no vaya por esa ruta en este caso particular, pero ciertamente es un enfoque útil. Cualquiera que esté mirando esto debería estar seguro de leer las conversaciones aquí, ya que también hay mucha información útil allí.

Gracias!

+0

¿se trata de un ejercicio académico solamente? – hvgotcodes

+0

¿Conoces la reflexión de Java? Le permite acceder a cualquier campo mediante cualquier método a través de nombres, pero es bastante complicado si lo desea para cada comando en su programa. – gigadot

+0

La reflexión es el camino a seguir, especialmente si no sabe a qué llamar hasta el tiempo de ejecución. – OscarRyz

Respuesta

10

Si conoce el conjunto de APIs que desee exponer, digamos que usted sabe quieren tener acceso a un método de longitud y un método iterador, puede definir una interfaz:

public interface TheInterfaceIWant { 
    int length(); 
    void quack(); 
} 

y que desea poder utilizar esta interfaz para acceder a métodos en los casos que no implementan esta interfaz correspondiente, puede utilizar Clases de proxy: http://download.oracle.com/javase/1.4.2/docs/api/java/lang/reflect/Proxy.html

Entonces crea un proxy

final Object aDuck = ...; 
TheInterfaceIWant aDuckWrapper = (TheInterfaceIWant) Proxy.newProxyInstance(
    TheInterfaceIWant.class.getClassLoader(), 
    new Class[] { TheInterfaceIWant.class }, 
    new InvocationHandler() { 
     public Object invoke(
      Object proxy, Method method, Object[] args) 
      throws Throwable { 
     return aDuck.getClass().getMethod(
      method.getName(), method.getParameterTypes()).invoke(aDuck, args); 
     } 
    }); 

Luego puede usar el contenedor como lo haría con el pato en un lenguaje de tipado dinámico.

if (aDuckWrapper.length() > 0) { 
    aDuckWrapper.quack(); 
} 

Aquí está un ejemplo ejecutable larga duración que imprime "Quack" cuatro veces utilizando un envoltorio:

import java.lang.reflect.*; 

public class Duck { 

    // The interface we use to access the duck typed object. 
    public interface TheInterfaceIWant { 
    int length(); 
    void quack(); 
    } 

    // The underlying instance that does not implement TheInterfaceIWant! 
    static final class Foo { 
    public int length() { return 4; } 
    public void quack() { System.out.println("Quack"); } 
    } 

    public static void main(String[] args) throws Exception { 
    // Create an instance but cast away all useful type info. 
    final Object aDuck = new Foo(); 

    TheInterfaceIWant aDuckWrapper = (TheInterfaceIWant) Proxy.newProxyInstance(
     TheInterfaceIWant.class.getClassLoader(), 
     new Class[] { TheInterfaceIWant.class }, 
     new InvocationHandler() { 
      public Object invoke(
       Object proxy, Method method, Object[] args) 
       throws Throwable { 
      return aDuck.getClass().getMethod(
       method.getName(), method.getParameterTypes()).invoke(aDuck, args); 
      } 
     }); 

    for (int n = aDuckWrapper.length(); --n >= 0;) { 
     // Calling aDuck.quack() here would be invalid since its an Object. 
     aDuckWrapper.quack(); 
    } 
    } 
} 
+0

Interesante. Así que, en última instancia, el rendimiento sería más o menos el mismo entre Proxy y reflection/Inspector, ya que todos usan la reflexión. Pero esto podría ser bueno para nosotros como un tipo de adaptador de una interfaz a otra. ¡Gracias! – mckamey

+1

@McKAMEY, sí. Desafortunadamente, hay un poco más sobrecarga. 'Method.getParameterTypes()' requiere una asignación de matriz y copia para todos los métodos sin parámetros. Si eso resulta ser una sobrecarga significativa, algunas memoraciones hábiles deberían ser capaces de reducir eso una vez que tenga algunos datos sobre los patrones de uso. –

+0

Más enlaces para esta técnica http://thinking-in-code.blogspot.com/2008/11/duck-typing-in-java-using-dynamic.html y http://download.oracle.com/javase/ 1.5.0/docs/guide/reflection/proxy.html – mckamey

0

Tome un vistazo a los métodos de java.lang.Class y en el API de reflexión:. Java.lang.reflect *

1

Otro método que me encontré con que aprovecha (? Abusos) tipo de borrado es bastante interesante :

http://rickyclarkson.blogspot.com/2006/07/duck-typing-in-java-and-no-reflection.html

no estoy seguro de que compro que esto es muy diferente del simple uso de las interfaces directamente, pero tal vez es útil para alguien más.

+1

En relación con esto: http://stackoverflow.com/questions/4288942/way-to-specify-multiple-interfaces-in-java/4288986 # 4288986 – OscarRyz

Cuestiones relacionadas