significado significa que objetos mutables inmutables inmutable inmutabilidad clases java class field immutability

significa - string inmutable java



¿Cómo gestionar la clase inmutable con LinkedList como un campo de instancia? (6)

Creo que el problema con este enfoque es que todavía puedo hacer getPersons (). Add (x). Para no permitir eso, debe crear un constructor que reciba la lista de personas y luego crear una instancia de su lista final interna como, por ejemplo, ImmutableList . El resto del código me parece bien.

EDITAR:

Como señala Davide Lorenzo MARINO, esto no es suficiente. También deberá devolver una copia detallada de la lista en el getter, de modo que los usuarios no puedan modificar su lista interna por la referencia devuelta por el getter.

Al hacer esto, puedes tener una lista normal de Linkedlist como tu lista interna (final privado). En su constructor, usted crea una copia en profundidad de la Lista y en su receptor devuelve una copia en profundidad de su Lista interna.

Tengo una clase inmutable llamada Employees como este:

public final class Employees { private final List<Person> persons; public Employees() { persons = new LinkedList<Person>(); } public List<Person> getPersons() { return persons; } }

¿Cómo mantengo esta clase inmutable?

Hice el campo private y final , y no proporcioné un método de establecimiento. ¿Es esto suficiente para lograr la inmutabilidad?


En este caso, yo preferiría no exponer la Lista o el Mapa en absoluto. Si pasa la colección, está rompiendo la encapsulación asumiendo que debe almacenarse como una Lista.

Si no expone la Lista, puede evitar la necesidad de envolverla o proporcionar una copia defensiva.

public final class Employees { private final List<Person> persons; public Employees() { persons = new LinkedList<Person>(); } public Person getPerson(int n) { return persons.get(n); } public int getPersonCount() { return persons.size(); } }

La razón por la que desea evitar las copias defensivas es que pueden llamarse mucho. Alguien podría escribir fácilmente (se sorprendería de la frecuencia con la que se escribe)

for (int i = 0; i < employees.getPersons().size(); i++) { Person p = employees.getPersons().get(i); }

Al dar métodos específicos, puede optimizar el código para ese caso de uso.


La respuesta se encuentra en los documentos - Una estrategia para definir objetos inmutables :

  1. No proporcione métodos de "establecimiento": métodos que modifican campos u objetos a los que se hace referencia por campos.

  2. Hacer todos los campos finales y privados.

  3. No permitir que las subclases anulen los métodos.

  4. Si los campos de la instancia incluyen referencias a objetos mutables, no permita que se cambien esos objetos :

    4.1. No proporcione métodos que modifiquen los objetos mutables.

    4.2. No comparta referencias a los objetos mutables. Nunca almacene referencias a objetos mutables externos pasados ​​al constructor; Si es necesario, cree copias y almacene referencias a las copias. Del mismo modo, cree copias de sus objetos mutables internos cuando sea necesario para evitar devolver los originales en sus métodos.


No, no es suficiente porque en java incluso las referencias se pasan por valor . Entonces, si List''s referencia de tu List''s escapa (lo que sucederá cuando llamen a get ), entonces tu clase ya no es inmutable .

Tienes 2 opciones:

  1. Cree una copia defensiva de su List y devuélvala cuando se llame a get.
  2. Envuelva su Lista como una Lista inmutable / No modificable y devuélvala (o reemplace su List original con esto, luego puede devolverla de manera segura sin tener que envolverla más)

Nota: Deberá asegurarse de que la Person sea ​​inmutable o crear copias defensivas para cada Person en la List


Podrías hacerlo aún mejor con

import java.util.Collections; ... public List<Person> getPersons() { return Collections.unmodifiableList( persons); }


Respuesta editada para explicar no solo el caso con una versión mutable de Person , sino también con una versión inmutable de Person .

Tu clase es mutable porque puedes hacer eso:

Employees employees = new Employees(); employees.getPersons().add(new Person());

Tenga en cuenta que si no cambia una lista de personas al constructor, si cambia su código para crear una clase inmutable, tendrá una clase no útil con una lista de personas siempre vacía, por lo que supongo que es necesario pasar una List<Person> a el constructor

Ahora hay dos escenarios, con diferentes implementaciones:

  • Person es inmutable
  • Person es mutable

Escenario 1 - La Person es inmutable.

Solo necesita crear una copia inmutable del parámetro de personas en el constructor .

También necesita getPersons la clase o, al menos, el método getPersons para asegurarse de que nadie proporciona una versión sobrescribible del método getPersons .

public final class Employees { private final List<Persons> persons; public Employees(List<Person> persons) { persons = Collections.unmodifiableList(new ArrayList<>(persons)); } public List<Employees> getPersons() { return persons; } }

Escenario 2 - La Person es mutable

getPersons crear una copia en profundidad de las persons en el método getPersons .

Necesitas crear una copia profunda de las persons en el constructor .

También necesita getPersons la clase o, al menos, el método getPersons para asegurarse de que nadie proporciona una versión sobrescribible del método getPersons .

public final class Employees { private final List<Persons> persons; public Employees(List<Person> persons) { persons = new ArrayList<>(); for (Person person : persons) { persons.add(deepCopy(person)); // If clone is provided // and creates a deep copy of person } } public List<Employees> getPersons() { List<Person> temp = new ArrayList<>(); for (Person person : persons) { temp.add(deepCopy(person)); // If clone is provided // and creates a deep copy of person } return temp; } public Person deepCopy(Person person) { Person copy = new Person(); // Provide a deep copy of person ... return copy; } }

Esta parte de la respuesta es para mostrar por qué una copia no profunda del personsParameter pasa al constructor puede crear versiones mutables de los Employees :

List<Person> personsParameter = new ArrayList<>(); Person person = new Person(); person.setName("Pippo"); personsParameter.add(person); Employees employees = new Employees(personsParameter); // Prints Pippo System.out.println(employees.getPersons().get(0).getName()); employees.getPersons().get(0).setName("newName"); // Prints again Pippo System.out.println(employees.getPersons().get(0).getName()); // But modifiyng something reachable from the parameter // used in the constructor person.setName("Pluto"); // Now it prints Pluto, so employees has changed System.out.println(employees.getPersons().get(0).getName());