2010-03-03 13 views
8

que tienen esta clase:¿Cuál es la forma preferida de asignar una colección desde un parámetro?

public MyClass { 
    public void initialize(Collection<String> data) { 
     this.data = data; // <-- Bad! 
    } 
    private Collection<String> data; 
} 

Esto es, obviamente, mal estilo, porque yo estoy presentando un estado mutable compartido. ¿Cuál es la forma preferida de manejar esto?

  • ¿Ignorarlo?
  • ¿Clonar la colección?
  • ...?

EDIT: para aclarar por qué esto es malo, imaginar esto:

MyClass myObject = new MyClass(); 
List<String> data = new ArrayList<String>(); 
myObject.initialize(data); // myObject.data.size() == 0 
data.add("Test"); // myObject.data.size() == 1 

Sólo almacenar la referencia plantea un modo de inyectar datos en el campo privado myObject.data, aunque debería ser totalmente privado.

Dependiendo de la naturaleza de MyClass, esto podría tener graves consecuencias.

+2

¿Cuándo y cómo está cambiando el estado? ¿Tu clase necesita observar el cambio? –

+0

@Jesse: se agregó un ejemplo a la pregunta. –

Respuesta

9

La mejor manera es clonar profundamente el parámetro. Por razones de rendimiento, esto generalmente no es posible. Además de eso, no todos los objetos se pueden clonar, por lo que una copia profunda puede arrojar excepciones y causar todo tipo de dolores de cabeza.

La siguiente mejor manera sería un clon de "copiar con la escritura". No hay soporte para esto en el tiempo de ejecución de Java.

Si cree que es posible que alguien muta la colección, realice una copia superficial utilizando el constructor de copia:

this.data = new HashSet<String> (data); 

esto va a resolver su problema (ya cadena es inmutable) pero fallará cuando el tipo de el conjunto es mutable

Otra solución es hacer siempre los conjuntos inmutables tan pronto como se los almacena en alguna parte:

Set<String> set = ... 
...build the set... 

// Freeze the set 
set = Collections.unmodifiableSet(set); 

// Now you can safely pass it elsewhere 
obj.setData (set); 

La idea aquí es a su vez en "colecciones de objetos de valor" tan pronto como sea posible. Cualquier persona que quiera cambiar la colección debe copiarla, cambiarla y luego guardarla de nuevo.

Dentro de una clase, puede mantener el conjunto mutable y lo envuelve en el captador (que debe hacer de todos modos).

Problemas con este enfoque: Rendimiento (pero probablemente no sea tan malo como era de esperar) y disciplina (se rompe si lo olvida en alguna parte).

+0

¡Buen punto sobre los elementos mutables! –

+0

@DR: Vaya. Fijo. –

0

Una idea será pasar los datos como una matriz de cadenas y crear el conjunto dentro de MyClass. Por supuesto, MyClass debería probar que los datos de entrada son válidos. Creo que esta es una buena práctica de todos modos.

Si la persona que llama de MyClass y MyClass en realidad trabaja con un Set<String>, podría considerar clonar la colección. Sin embargo, el Set debe ser construido de alguna manera. Preferiría trasladar esta responsabilidad a MyClass.

3
  • cheque nulo (si desea restringir nulo)
  • De cualquier copia defensiva (estado si no desea que se Compartido)
  • o como lo hizo (si es una vista en vivo en los datos es útil)

Depende en gran medida de sus requisitos.

Editado: Ignorando debería ser ninguna opción. Silent fail es, bueno ... una pesadilla de depuración.

1
public class Foo { 
    private final Collection collection = new ArrayList(); 
    public void initialise(final Collection collection) { 
     this.collection.addAll(collection); 
    } 
} 
1

Lo siento por no hacer frente a su problema directamente, pero nunca pasar directamente a una colección de un método de selección de frijol setXXX(). En su lugar, me gustaría hacer:

private final List<MyClass> theList; 

public void addXxx(MyClass item) { ... } 
public void removeXxx(MyClass item) { ... } // or index. 

public void Iterator<MyClass> iterateXxx() { 
    return Collections.unmodifiableList(theList).iterator(); 
} 

me gustaría ir para copiar defensiva clonación/profundo sólo si estoy seguro de que no habría efectos secundarios de su uso, y en cuanto a la velocidad, no se referiría a mí mismo con ya que, en aplicaciones empresariales, la fiabilidad tiene una prioridad 10 veces mayor que la velocidad. ;-)

Cuestiones relacionadas