valores react objetos mutables mutabilidad inmutables inmutable inmutabilidad ejemplos dios java object immutability member getter-setter

react - objetos mutables e inmutables java



Objeto inmutable con variable miembro de ArrayList: ¿por qué se puede cambiar esta variable? (9)

Tengo una clase con varias variables miembro. Hay un constructor y hay métodos getter, pero no métodos setter. De hecho, este objeto debe ser inmutable.

public class Example { private ArrayList<String> list; }

Ahora me di cuenta de lo siguiente: cuando obtengo la lista de variables con un método getter, puedo agregar nuevos valores y así sucesivamente: puedo cambiar el ArrayList . Cuando llamo a la próxima vez get() para esta variable, se devuelve el ArrayList modificado. ¿Cómo puede ser esto? ¡No lo puse de nuevo, solo trabajé en ello! Con un String este comportamiento no es posible. Entonces, ¿cuál es la diferencia aquí?


Collections.unmodifiableList () hace que la lista no sea identificable. Lo que de nuevo crea una nueva lista de matriz final y anula los métodos de agregar, eliminar, agregar y borrar para lanzar una excepción de operación no admitida. Es un hack de clases de colecciones. Pero en el momento de la compilación no te impide agregar y eliminar cosas. Prefiero ir con la clonación de esa lista. Lo que me puede ayudar a mantener mi objeto existente inmutable y no me cuesta crear una nueva lista. Lea la diferencia entre la clonación y el nuevo operador ( http://www.javatpoint.com/object-cloning ). También ayudará a fallar mi código en tiempo de ejecución.


Como las otras respuestas dicen que el Objeto que devuelves de los captadores todavía es mutable.

Puede convertir la Lista en inmutable decorándola con la clase Colecciones:

list = Collections.unmodifiableList(list);

Si devuelve esto a los clientes, no podrán agregar o quitar elementos. Sin embargo, aún pueden sacar elementos de la lista, así que debes asegurarte de que también sean inmutables, ¡si eso es lo que buscas!


El hecho de que la referencia a la lista sea inmutable no significa que la lista a la que se refiere sea ​​inmutable.

Incluso si la list se hiciera final esto estaría permitido.

// changing the object which list refers to example.getList().add("stuff");

pero esto no permitía:

// changing list example.list = new ArrayList<String>(); // assuming list is public

Para hacer que la lista sea inmutable (también impida la primera línea), le sugiero que use Collections.unmodifiableList :

public class Example { final private ArrayList<String> list; Example(ArrayList<String> listArg) { list = Collections.unmodifiableList(listArg); } }

(Tenga en cuenta que esto crea una vista no modificable de la lista. Si alguien se aferra a la referencia original, la lista aún puede modificarse a través de eso).

Con una cadena este comportamiento no es posible. Entonces, ¿cuál es la diferencia aquí?

Esto se debe a que una String ya es inmutable (no modificable) tal como lo sería la lista si la convirtiera en una Lista no modificable.

Comparación:

String data structure | List data structure .-------------------------+------------------------------------. Immutable | String | Collection.unmodifiableList(...) | -----------+-------------------------+------------------------------------| Mutable | StringBuffer | ArrayList | ''-------------------------+------------------------------------''


La clave es comprender que no está cambiando la cadena ; está cambiando la cadena que hace referencia a la lista .

Para decirlo de otra manera: si saco un dólar de su billetera y lo sustituyo por un centavo, no he cambiado ni el dólar ni la moneda de diez centavos, solo he cambiado el contenido de su billetera.

Si desea una vista de solo lectura en la lista, consulte Collections.unmodifiableList . Por supuesto, eso no impedirá que la lista que está terminando de cambiarse, pero evitará que cualquiera que solo tenga una referencia a la lista no modificable modifique el contenido.

Para una lista verdaderamente inmutable, mire la clase ImmutableList Guava .


La referencia de la lista es inmutable, pero no la lista. Si desea que la lista en sí sea inmutable, considere usar ImmutableList


Otra solución es devolver una copia del arrailista, no la lista de matrices real como este código

import java.util.ArrayList; import java.util.List; public final class ImmutableExample { private final List<String> strings = new ArrayList<String>(); public ImmutableExample() { strings.add("strin 1"); strings.add("string 2"); } public List<String> getStrings() { List<String> newStrings = new ArrayList<String>(); strings.forEach(s -> newStrings.add(s)); return newStrings; } public static void main(String[] args) { // TODO Auto-generated method stub ImmutableExample im = new ImmutableExample(); System.out.println(im.getStrings()); im.getStrings().add("string 3"); System.out.println(im.getStrings()); } }


Para obtener una lista realmente inmutable, tendrá que hacer copias en profundidad de los contenidos de la lista. UnmodifiableList solo haría la lista de referencias un tanto inmutable. Ahora hacer una copia en profundidad de la Lista o matriz será difícil de memorizar con el tamaño cada vez mayor. Puede utilizar la serialización / deserialización y almacenar la copia en profundidad de la matriz / lista en un archivo temporal. El establecedor no estaría disponible ya que el miembro variable debe ser inmutable. El captador serializaría la variable miembro en un archivo y luego la desializaría para obtener una copia profunda. La serialización tiene una naturaleza innata de ir a las profundidades de un árbol de objetos. Sin embargo, esto aseguraría una total inmutabilidad a un costo de rendimiento

package com.home.immutable.serial; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; public final class ImmutableBySerial { private final int num; private final String str; private final TestObjSerial[] arr; ImmutableBySerial(int num, String str, TestObjSerial[] arr){ this.num = num; this.str = str; this.arr = getDeepCloned(arr); } public int getNum(){ return num; } public String getStr(){ return str; } public TestObjSerial[] getArr(){ return getDeepCloned(arr); } private TestObjSerial[] getDeepCloned(TestObjSerial[] arr){ FileOutputStream fos = null; ObjectOutputStream oos = null; FileInputStream fis = null; ObjectInputStream ois = null; TestObjSerial[] clonedObj = null; try { fos = new FileOutputStream(new File("temp")); oos = new ObjectOutputStream(fos); oos.writeObject(arr); fis = new FileInputStream(new File("temp")); ois = new ObjectInputStream(fis); clonedObj = (TestObjSerial[])ois.readObject(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } finally { try { oos.close(); fos.close(); } catch (IOException e) { e.printStackTrace(); } } return clonedObj; } }


Por lo tanto, no debe proporcionar un método getter para la lista si desea evitar que se modifique.

Sus objetos permanecen intactos ya que no tenías setters. Pero lo que haces es quitar / agregar objetos nuevos / diferentes y esto está bien.


Usted está devolviendo una referencia a la list . Y la list no es inmutable.

Si no desea que se cambie su lista, devuelva una copia de la misma:

public List<String> get() { return new ArrayList<String>(this.list); }

O puede devolver una lista no modificable :

public List<String> get() { return Collections.unmodifiableList(this.list); }