2009-04-26 17 views
6

En mi proyecto Java, tengo un vector de varios tipos de comerciantes. Estos diferentes tipos de operadores son subclases de la clase Trader. En este momento, tengo un método que toma un comerciante como argumento y lo almacena 50 o más veces en el vector. Estoy teniendo problemas porque almacenar el mismo objeto 50 veces es solo almacenar 50 referencias del mismo objeto. Necesito almacenar 50 copias del objeto. Investigué sobre la implementación de Clone, pero no quiero que los programadores que definen un tipo de Trader tengan que preocuparse por hacer clonable su clase. Además, como se señala en this page, la implementación del clon crea todo tipo de problemas. No creo que un constructor de copia funcione tampoco, porque si definiera uno en la clase Trader, no sabría el tipo de Trader que estaba copiando y simplemente crearía un Trader genérico. ¿Que puedo hacer?¿Hay alguna alternativa para implementar Clone en Java?

Editar: Realmente no quiero hacer copias exactas de un objeto determinado. Lo que realmente intento hacer es agregar un cierto número de operadores al vector. El problema es que el usuario necesita especificar en un argumento qué tipo de Trader quiere agregar. Aquí está un ejemplo de lo que estoy tratando de hacer: (aunque mi sintaxis es completamente imaginaria)

public void addTraders(*traderType*) 
{ 
    tradervect.add(new *traderType*()) 
} 

¿Cómo puedo lograr algo como esto en Java?

Respuesta

2

simplemente añada una copia método abstracto. Puede usar tipos de retorno covariantes para que el tipo derivado se especifique para devolver una instancia derivada, que puede o no ser importante.

public interface Trader { 
    Trader copyTrader(); 
    ... 
} 


public final class MyTrader implements Trader { 
    MyTrader copyTrader() { 
     return new MyTrader(this); 
    } 
    ... 
} 

A veces es posible que desee genéricamente acuerdo con una colección de tipo derivado de Trader que necesita para clonar y luego devolver la colección correctamente escrito. Para que usted pueda utilizar los genéricos de una manera idiomática:

public interface Trader<THIS extends Trader> { 
    THIS copyTrader(); 
    ... 
} 


public final class MyTrader implements Trader<MyTrader> { 
    public MyTrader copyTrader() { 
     return new MyTrader(this); 
    } 
    ... 
} 
+0

No veo cómo puedo hacer que la clase Trader genérica sea una interfaz porque la clase Trader tiene métodos que deben ser accesibles. Hacer una interfaz significaría que todos esos métodos de bajo nivel no permitirían que esos métodos se definan en la clase Trader. –

+0

Si el comerciante es una clase abstracta o interfaz es irrelevante. En general, se prefiere una interfaz a una clase abstracta. También prefiere la delegación a la herencia. –

0

Una opción: si puede hacer que los objetos sean serializables, puede serializarlos y luego deserializarlos para hacer una copia, similar a lo que sucede cuando se pasa un objeto por encima de RMI.

método de copia rápida:

public MyObject copy() { 
    ObjectOutputStream oos = null; 
    ObjectInputStream ois = null; 
    try { 
     ByteArrayOutputStream bos = new ByteArrayOutputStream(); 
     oos = new ObjectOutputStream(bos); 
     oos.writeObject(this); 
     oos.flush(); 
     ByteArrayInputStream bin = 
      new ByteArrayInputStream(bos.toByteArray()); 
     ois = new ObjectInputStream(bin); 
     return (MyObject)ois.readObject(); 
    } catch(Exception e) { 
     return null; 
    } finally { 
     try { 
      oos.close(); 
      ois.close(); 
     } catch (Exception e) { 
      return null; 
     } 
    } 
} 
+0

¿Por qué el voto a favor? Es una forma adecuada de invocar un objeto complejo. Sin embargo, la administración de recursos está total y completamente rota. –

+0

¿Es esa la única solución? Parece que no puedo convencerme de que una operación tan simple causaría tanto alboroto. ¿Qué pasaría si implementara Clone para permitir la copia? ¿Sería eso posible? –

+0

@Tom: No estoy seguro, pero usar serialización/deserialización para la clonación es tan sutil como usar memcpy en lugar de copiar constructores en C++. – Uri

-2

Una forma es hacer que sea una clase final como propia cadena de Java, que hará que cualquier cambio en un objeto de comerciante de clase para crear una nueva copia en la memoria , pero hará que sea imposible subclasificarlo.

Otra (mejor) forma es usar un método de fábrica para crear y copiar objeciones de Trader, lo que implica que no debe permitir el uso del constructor predeterminado, es decir, hacerlo privado. De esta forma puede controlar el número de instancias que tiene la clase. Ver http://en.wikipedia.org/wiki/Factory_method

public class Trader { 

    /* prevent constructor so new cant be used outside the factory method */ 
    private Trader() { 
    } 

    /* the factory method */ 
    public static Trader createTrader(int whatKindOfTrader) { 

     switch (whatKindOfTrader) { 
     case 0: 
      return new Trader1(); // extends Trader 
     case 1: 
     default: 
      return new Trader2(); // extends Trader 
     } 
     return new Trader3(); // or throw exception 
    } 
} 

incluso se podría especificar otro método sobrecargado, o un segundo argumento que toma un comerciante y lo copia en una nueva, sustituyendo así clon. Por cierto, es posible que desee sobrescribir el método clone() y lanzar CloneNotSupportedException, para evitar la clonación de objetos predeterminada.

+0

Quieres decir inmutable en lugar de solo una clase final. La inmutabilidad es probable que vaya un poco lejos para un objeto "comerciante". –

+0

No fui inmutable, pero definitivo. HAY una diferencia, y personalmente nunca la usaría. Pero, el método de Fábrica es uno de mejores prácticas, y es un patrón de diseño común Por cierto, el objeto inmutable simplemente no es útil en este caso. – Azder

2

No tengo claro por qué querría almacenar aproximadamente 50 clones del mismo objeto a menos que el operador original sirva como prototipo (ver patrón) para los comerciantes posteriores.

Si desea hacer una copia exacta de un objeto, debe tener en cuenta el problema del polimorfismo. Si a las personas que subclasan su clase se les permite agregar miembros del estado, entonces usted ya tiene suficiente dolor de cabeza con funciones como equals y compareTo, ese clon es un caso más en el que necesita requerir un manejo especial.

No estoy de acuerdo en que el clon sea siempre malo, a veces es necesario. Sin embargo, en situaciones de subclases, muchas cosas se vuelven complicadas.

Recomendaría que leyeras (cuando tengas la oportunidad) Bloch "Effective Java", que cubre muchos de sus temas. La opinión de Bracha es que no es una buena idea dejar que otros amplíen sus clases, y que si lo hace, debe documentar muy bien lo que deberían hacer y esperar que sigan sus instrucciones. Realmente no hay una manera de eludir eso. Es posible que también desee que sus comerciantes sean inmutables.

Adelante un implementar Clone normalmente, e indique muy claramente en el encabezado de la clase que cualquier persona que herede de su operador y que agregue miembros de estado debe implementar métodos X (especifique cuál).

Encontrar trucos para eludir un clonable real y todavía clonar no va a superar el problema de la herencia. No hay una bala de plata aquí.

+0

Tu respuesta me hizo pensar. Ver la edición que hice para más información.:) –

+0

Espera, ¿estás diciendo básicamente que si X amplía Trader, y alguien le pasó una instancia de X a tu función, quieres obtener otra X? – Uri

+0

Si desea este comportamiento, utilice el (horrible) getClass.newInstance idioma:. traderType.getClass() newInstance() Usted tiene que asegurarse de que siempre hay un constructor público, y es probable que algunos quieren init método que siempre puede llamar para llevarlo a un estado legítimo. – Uri

0

Uri está en lo cierto, el polimorfismo con el estado abre una gran lata de gusanos.

Creo que la subclase Cloneable y overrideing clone() es probablemente la forma más sencilla de hacerlo. Puede, creo, hacer que el tipo de retorno sea covariante.

Cuestiones relacionadas