2010-12-09 22 views
10

Tengo pares de clases donde los campos de uno son un subconjunto de los campos de otro y los captadores de las clases de superconjunto se llaman predeciblemente (getFoo()). ¿Hay alguna manera de eficientemente copiar todos los campos comunes desde la clase de superconjunto a la clase de subconjunto, o al menos autogenerar el código para hacerlo.Copie campos entre clases similares en java

Debo señalar que:

  • Por diversas razones, no puedo editar las clases superconjunto, ni puedo sólo los utilizan a lo largo para evitar tener que hacer la copia de datos.
  • Posiblemente pueda crear nuevos métodos en las clases de subconjunto, pero no puedo cambiar sus campos.
  • Tenemos docenas de estos pares, y algunas de las clases tienen muchos campos, por lo que hacer esto a mano es difícil de decir por lo menos.
  • Un colega ha ideado un método para crear un método de copia genérico que utiliza reflexión de java para tomar dos clases, recorrer los campos como cadenas, manipular cadenas para determinar el nombre del captador y luego ejecutarlo para establecer automáticamente el campo en la clase de subconjunto. Es horrible, pero parece funcionar. Realmente espero que haya una mejor manera.

Editar: un código simple como solicitado

public class SuperClass { 
    private int foo; 
    private int bar; 
    private float bat; 
    public int getFoo() { return foo; } 
    public int getBar() { return bar; } 
    public float getBat() { return bat; } 
} 

public class SubClass { 
    private int foo; 
    private float bat; 
} 

//wanted 
public static copySuperFieldsToSubMethod(Object super, Object sub) { ??? } 

// also acceptable would be some way to autogenerate all the assignment 
// functions needed 
+0

Si pudieras editar las clases de superconjunto simplemente podrías hacer que amplíen tus clases de subconjunto y hereden todos sus campos y métodos en la moda OO clásica. ¿Por qué no puedes editar las clases de superconjunto? – Asaph

+0

@Asaph - lamentablemente, las clases de superconjunto son parte de una biblioteca externa que se desarrolló por separado y que ahora necesito poder transformar en las clases creadas para este proyecto específico que no utilizan mucha de la información en las más grandes . – Dusty

+0

¿Qué pasa si se da vuelta la relación al revés y se heredan las clases de subconjunto de la clase de superconjunto? Puede anular cualquier método relacionado con campos que no necesita con cuerpos vacíos. No es muy elegante, pero al menos puedes volver a usar el código sin copiar/pegar, reflexión o generación de código. – Asaph

Respuesta

12

Usted podría utilizar la clase BeanUtils en el Spring Framework para hacer esto. Puede que no sea necesariamente más eficiente que tu técnica basada en la reflexión, pero es ciertamente simple de codificar. Espero que todo lo que tendría que hacer es:

BeanUtils.copyProperties(source, target); 

Javadoc de este método está disponible en http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/beans/BeanUtils.html#copyProperties(java.lang.Object,%20java.lang.Object)

Si eso no le conviene, también se puede considerar el uso de BeanWrapper/BeanWrapperImpl en el Spring Framework para iterar a través de las propiedades de tus clases. Eso sería más simple que usar API de reflexión de bajo nivel.

+1

Wow, eso es exactamente lo que estoy buscando, pero no creo que sea factible recurrir al Spring Framework solo por este único problema. – Dusty

+4

Apache Commons BeanUtils podría ser adecuado entonces, es un poco más ligero que Spring. Tiene una clase y método con un nombre idéntico que se ve sospechosamente similar, aunque nunca lo he usado. Consulte http://commons.apache.org/beanutils/ – gutch

+0

La versión de Spring funciona mejor en clases relacionadas a través de la herencia. – raffian

0

¿Puede proporcionarnos algún código de muestra de su aplicación que represente el escenario que ha mencionado en su publicación? En este momento, la reflexión parece ser la mejor, ya que le permite inspeccionar a los miembros de la clase en tiempo de ejecución.

+0

Agregué un código de ejemplo, pero genérico (no puedo mostrar el código real) – Dusty

+0

El enfoque de @ gutch se ajusta exactamente a sus necesidades, pero desafortunadamente no puede usarlo. –

2

Si desea realizar la tarea de manera eficiente (en términos de rendimiento en tiempo de ejecución), entonces la codificación manual de la copia con getters y setters es el camino a seguir. A menos que haya algo funky sobre los métodos getter o setter, sus cuerpos estarán incrustados para que sean tan rápidos como para hacer asignaciones de campo.

El enfoque reflexivo (por ejemplo, usando una clase existente como BeanUtils) es menos codificante, pero probablemente un orden de magnitud más lento que llamar a getters y setters de una manera simple. Si intenta implementar esto usted mismo, puede encontrarse con más trabajo de lo que esperaba, especialmente si su clase/método de copia reflectiva tiene que lidiar con métodos sobrecargados, herencia, conversión de valores, boxeo/desempaquetado, etc.

Con el enfoque de generación de código, necesita equilibrar el esfuerzo y la complejidad de implementar la generación de código (usando la tecnología que elija) versus el esfuerzo de escribir los métodos de copia a mano. Probablemente probablemente no alcanzará un punto de equilibrio con el enfoque de generación de código antes de las 20 clases ...y muchos más si no está familiarizado con la tecnología.

1

Escribo una herramienta java simple para autogenerar el código fuente para las clases que pueden rellenar los campos de subconjuntos con los campos comunes de superconjunto. Esta herramienta utilizará la reflexión para obtener los nombres de los métodos getter y setter. El resto son operaciones de cadena (triviales) para "escribir" un archivo fuente en la memoria y almacenarlo en un archivo *.java. Compile todos estos archivos autogenerados y agregue los archivos de clase a classpath.

La clase podría tener este aspecto:

class AClassToBClassPopulator implements Populator { 
    @Overwrite 
    public void populate(Object superSet, Object subSet) { 
     subSet.setFieldA(superSet.getFieldA()); 
     subSet.setFieldB(superSet.getFieldB()); 
     // .. and so on. The method body is created through reflection 
    } 
} 
0

Esto es obviamente una tarea de reflexión de Java y mientras que otros ya han sugerido válida aunque quizás un soluciones de peso pesado bits, aquí hay uno más:

Hace aproximadamente un año, escribí una biblioteca modificadora de propiedades JavaBean pequeña llamada BeanPropertyController. Si bien no lo recomiendo específicamente a nadie, creo que la clase homónima de la biblioteca (see source) puede utilizarse como referencia para adoptar una funcionalidad similar a sus necesidades. Como un ejemplo rápido, así es como yo solía BPC hacer (casi!) Lo que preguntas:

// somewhere in code... 
SuperClass a = new SuperClass(); 
a.foo = 101; 
a.bar = 102; 
a.bat = 103f; 

SubClass b = new SubClass(); 
b.foo = 201; 
b.bat = 202f; 

BeanPropertyController fromB = BeanPropertyController.of(b, ExtractionDepth.QUESTIMATE); 
BeanPropertyController toA = BeanPropertyController.of(a, ExtractionDepth.QUESTIMATE); 

// This is where the magic happens: 
for (String propertyName : fromB.getPropertyNames()) { 
    toA.mutate(propertyName, fromB.access(propertyName)); 
} 
a = (SuperClass) toA.getObject(); 
b = (SubClass) fromB.getObject(); 

System.out.println("SuperClass' foo="+a.foo+" bar="+a.bar+" bat="+a.bat); 
System.out.println("SubClass' foo="+b.foo+" bat="+b.bat); 

Esto muestra

SuperClass' foo=201 bar=102 bat=202.0 
SubClass' foo=201 bat=202.0 

Por lo tanto, lo que sugiero es que usted vaya a la URL que vinculé y adaptar esta pieza de código a sus necesidades. Estoy bastante seguro de que no necesita los diversos métodos de creación de instancias, proveedores de valores predeterminados, etc. que he incluido. Y sí, BPC puede considerarse obsoleto.

Cuestiones relacionadas