what valores son react mutabilidad los inmutables inmutable inmutabilidad immutable clases java

java - valores - mutable e inmutable



¿Por qué hacer copias defensivas en captadores dentro de clases inmutables? (7)

¿Por qué debería considerar hacer copias defensivas en métodos de obtención en mi clase inmutable sin mutadores?

Esto solo es útil si los objetos devueltos no son ya inmutables.

Una inmutabilidad profunda como esta es útil para evitar cambios en cualquier objeto en poder de la clase inmutable.

Considera que tienes un caché de objetos. Cada vez que se recupera un objeto de la memoria caché y se modifica, corre el riesgo de modificar también el valor en la memoria caché.

¿Por qué debo hacer mis campos finales además de privados?

Simplemente para ayudarlo a lograr la inmutabilidad y evitar que los valores se modifiquen accidentalmente o en forma generosa una vez establecidos (p. Ej., Mediante subclasificación).

Esta pregunta es sobre buenas prácticas de programación y evitar posibles agujeros.
Leí Effective Java de Joshua Bloch y esto es lo que me pregunto:
¿Por qué debería considerar hacer copias defensivas en métodos de obtención en mi clase inmutable sin mutadores?
Y segundo: ¿por qué debo hacer que mis campos sean finales además de privados ? ¿Se trata solo de rendimiento (no de seguridad)?


1 Si no hace una copia defensiva, puede dejar que su objeto sea entregado, una vez que lo entregue, su clase no será inmutable, la persona que llama puede cambiar el objeto.

public class Immutable { private List<Object> something; public List<Object> getSomething(){ return something; // everything goes for a toss, once caller has this, it can be changed } }

2 Si su campo es solo privado y no definitivo, significa que puede reinicializar el campo, pero si su campo es definitivo, se iniciará solo una vez y no varias veces y logrará inmutabilidad.


Creo que este es el caso que justifica estas afirmaciones:

public class Immutable { private final String name; private Date dateOfBirth; public Immutable(String name, Date dateOfBirth) { this.name = name; this.dateOfBirth = dateOfBirth; } public String getName() { return name; } public Date getDateOfBirth() { return dateOfBirth; } }

getName() está bien ya que también devuelve un objeto inmutable. Sin embargo, el método getDateOfBirth() puede interrumpir la inmutabilidad porque el código del cliente puede modificar el objeto devuelto, por lo que también se modifica el objeto Immutable :

Immutable imm = new Immutable("John", new Date()); imm.getName(); //safe Date dateOfBirth = imm.getDateOfBirth(); //hundreds of lines later dateOfBirth.setTime(0); //we just modified `imm` object

Es seguro devolver objetos y elementos primarios inmutables (ya que se devuelven por valor). Sin embargo, necesita hacer copias defensivas de objetos mutables, como Date :

public Date getDateOfBirth() { return new Date(dateOfBirth.getTime()); }

y envuelva las colecciones en vistas inmutables (si son mutables), por ejemplo, vea Collections.unmodifiableList() :

public List<Integer> getSomeIds() { return Collections.unmodifiableList(someIds); }


Debería hacer una copia defensiva solo si el objeto que está devolviendo en su captador es mutable, porque el cliente podría cambiar su estado de objeto.

Con respecto a la pregunta final, no es estrictamente necesario hacer que los campos sean definitivos, sino que se conviertan en una concesión final que no se puede modificar una vez que se crea el objeto.

De hecho, si necesita modificar algunos campos de su objeto después de crearlo, esto también está bien, pero debe asegurarse de que el código del cliente no pueda distinguir que el estado del objeto ha sido alterado. Lo que tiene que hacer inmutable es el estado externo visible del objeto, no el estado interno.

Por ejemplo, la clase String no calcula su código de hash en la creación, la calcula la primera vez que se necesita, y luego la almacena en un campo privado mutable.

Supongo que su clase se declara definitiva o solo tiene constructores privados, de lo contrario, subclases de la misma podrían alterar sus campos no finales de manera impredecible ...

Algún código de ejemplo para aclarar:

public final class Question { //final class to assure that inheritor could not // make it mutable private int textLenCache = -1; //we alter it after creation, only if needed private final String text; private Date createdOn; public Immutable(String text, Date createdOn) { Date copy=new Date(createdOn.getTime() ) //defensive copy on object creation //Ensure your class invariants, e.g. both params must be non null //note that you must check on defensive copied object, otherwise client //could alter them after you check. if (text==null) throw new IllegalArgumentException("text"); if (copy==null) throw new IllegalArgumentException("createdOn"); this.text= text; //no need to do defensive copy an immutable object this.createdOn= copy; } public int getTextLen() { if (textLenCache == -1) textLenCache=text.length(); //client can''t see our changed state, //this is fine and your class is still //immutable return textLenCache; } public Date getCreatedOn() { return new Date(createdOn.getTime()); //Date is mutable, so defend! } }

EDITAR :

La copia defensiva en los parámetros mutables del constructor es necesaria por dos razones:

  1. el código del cliente podría cambiar el estado del parámetro después de crear su objeto. Necesitas hacer una copia del parámetro para evitar esta posibilidad. Por ejemplo, piense si String object constructor String (char [] value) utilizó la matriz de caracteres que proporcionó sin copiarla: podrá modificar el contenido de la cadena modificando la matriz de caracteres que proporciona en el constructor.

  2. desea asegurarse de que el estado del objeto mutable no cambie entre el momento en que verifique la restricción y el momento en que lo copie en su campo. Para esta razón, siempre debe verificar la restricción en su copia local del parámetro.


Las referencias de objetos en Java son promiscuas; Si se pasa una referencia de objeto a un método, ese método no tendrá forma de saber quién más podría tener una referencia a ese mismo objeto, ni tampoco qué intentarán hacer con él. Del mismo modo, si un método devuelve una referencia de objeto, no se sabe qué puede hacer el destinatario con él. En consecuencia, si un tipo permite que cualquier persona con una referencia lo mute, la única forma en que una entidad que tiene una referencia a un objeto de este tipo puede asegurarse de que no será mutada es mantener una referencia a un objeto privado, que fuera nunca ha tenido. , y nunca obtendrá, una referencia a.

Una alternativa al estilo de copia defensiva que se muestra aquí (construir una nueva instancia de objeto cada vez que se solicita información) es hacer que el método de recuperación de información de un objeto acepte un objeto mutable y rellene ese objeto con la información que se recupera. Este enfoque requiere que el código que solicita que la información construya un objeto (probablemente en blanco) acepte la información antes de llamar al método para recuperarla, pero si, por ejemplo, se usa un bucle para recuperar, examinar brevemente y descartar 100 piezas de En cuanto a la información, puede ser más rápido construir un objeto que se pueda reutilizar cada vez a través del bucle, que construir 100 objetos nuevos, cada uno de los cuales se usará solo brevemente.


Modifiqué ligeramente la respuesta de Tomasz Nurkiewicz, para demostrar por qué hacer que dateOfBirth final no lo defienda de ser modificado por una clase cliente:

public class Immutable { private final String name; private final Date dateOfBirth; public Immutable(String name, Date dateOfBirth) { this.name = name; this.dateOfBirth = dateOfBirth; } public String getName() { return name; } public Date getDateOfBirth() { return dateOfBirth; } public static void main(String[] args) { //create an object Immutable imm = new Immutable("John", new Date()); System.out.println(imm.getName() + " " + imm.getDateOfBirth()); //try and modify object''s intenal String name = imm.getName(); //safe because Integer is immutable name = "George"; //has no effect on imm Date dateOfBirth = imm.getDateOfBirth(); dateOfBirth.setTime(0); //we just modified `imm` object System.out.println(imm.getName() + " " + imm.getDateOfBirth()); } } **Output:** John Wed Jan 13 11:23:49 IST 2016 John Thu Jan 01 02:00:00 IST 1970


Si bien la clase adjunta puede ser inmutable, las referencias devueltas por sus métodos de obtención pueden ser mutables, lo que permite al llamante modificar el estado transitivo del inmutable. Ejemplo:

public class MyImmutable { private final StringBuilder foo = new StringBuilder("bar"); public StringBuilder getFoo() { return foo; // BAD! } }

Debe usar privado para la encapsulación (evite que las clases dependan de los detalles de implementación de su clase). Debe usar final para asegurarse de no modificar el campo por error si no está destinado a ser modificado (y sí, podría ayudar al rendimiento).