¿Deben todas las propiedades de un objeto inmutable ser definitivas?

¿Los objetos inmutables deben tener todas las propiedades como final ?

Según yo, no. Pero no sé, si estoy en lo correcto.

La principal diferencia entre un objeto inmutable (todas las propiedades es definitivo) y un objeto efectivamente inmutable (las propiedades no son definitivas, pero no pueden modificarse) es una publicación segura.

Puede publicar de forma segura un objeto inmutable en un contexto de múltiples subprocesos sin tener que preocuparse por agregar sincronización, gracias a las garantías proporcionadas por el Modelo de memoria de Java para los campos finales :

los campos finales también permiten a los progtwigdores implementar objetos inmutables seguros para subprocesos sin sincronización. Un objeto inmutable seguro de subprocesos se ve como inmutable por todos los subprocesos, incluso si se usa una carrera de datos para pasar referencias al objeto inmutable entre subprocesos. Esto puede proporcionar garantías de seguridad contra el uso indebido de una clase inmutable por código incorrecto o malicioso. los campos finales deben usarse correctamente para proporcionar una garantía de inmutabilidad.

Como nota al margen, también permite forzar la inmutabilidad (si intentas mutar esos campos en una versión futura de tu clase porque has olvidado que debe ser inmutable, no comstackrá).


Aclaraciones

  • Hacer que todos los campos de un objeto sean definitivos no lo hace inmutable; también debe asegurarse de que (i) su estado no puede cambiar (por ejemplo, si el objeto contiene una final List , no hay operaciones de mutación (agregar, eliminar). ..) debe hacerse después de la construcción) y (ii) no dejes escapar this durante la construcción
  • Un objeto efectivamente inmutable es thread safe una vez que ha sido publicado con seguridad
  • Ejemplo de publicación insegura:

     class EffectivelyImmutable { static EffectivelyImmutable unsafe; private int i; public EffectivelyImmutable (int i) { this.i = i; } public int get() { return i; } } // in some thread EffectivelyImmutable.unsafe = new EffectivelyImmutable(1); //in some other thread if (EffectivelyImmutable.unsafe != null && EffectivelyImmutable.unsafe.get() != 1) System.out.println("What???"); 

    Este progtwig podría, en teoría, imprimir What??? . Si fuera final, no sería un resultado legal.

Puede garantizar fácilmente la inmutabilidad mediante encapsulación sola, por lo que no es necesario :

 // This is trivially immutable. public class Foo { private String bar; public Foo(String bar) { this.bar = bar; } public String getBar() { return bar; } } 

Sin embargo, también debe garantizarlo mediante encapsulación en algunos casos, por lo que no es suficiente :

 public class Womble { private final List cabbages; public Womble(List cabbages) { this.cabbages = cabbages; } public List getCabbages() { return cabbages; } } // ... Womble w = new Womble(...); // This might count as mutation in your design. (Or it might not.) w.getCabbages().add("cabbage"); 

No es una mala idea hacerlo para detectar algunos errores triviales y demostrar claramente su intención, pero “todos los campos son definitivos” y “la clase es inmutable” no son afirmaciones equivalentes.

Los objetos inmutables NO DEBEN ser modificados de ninguna manera después de su creación. final, por supuesto, ayuda a lograr eso. Usted garantiza que nunca serán cambiados. PERO, ¿y si tienes una matriz dentro de tu objeto que es definitiva? Por supuesto, la referencia no es modificable, pero los elementos sí lo son. Mire aquí casi la misma pregunta que hice también:

Enlazar

Simplemente declarar un objeto como final no lo hace intrínsecamente inmutable. Tomemos como ejemplo esta clase :

 import java.util.Date; /** * Planet is an immutable class, since there is no way to change * its state after construction. */ public final class Planet { public Planet (double aMass, String aName, Date aDateOfDiscovery) { fMass = aMass; fName = aName; //make a private copy of aDateOfDiscovery //this is the only way to keep the fDateOfDiscovery //field private, and shields this class from any changes that //the caller may make to the original aDateOfDiscovery object fDateOfDiscovery = new Date(aDateOfDiscovery.getTime()); } /** * Returns a primitive value. * * The caller can do whatever they want with the return value, without * affecting the internals of this class. Why? Because this is a primitive * value. The caller sees its "own" double that simply has the * same value as fMass. */ public double getMass() { return fMass; } /** * Returns an immutable object. * * The caller gets a direct reference to the internal field. But this is not * dangerous, since String is immutable and cannot be changed. */ public String getName() { return fName; } // /** // * Returns a mutable object - likely bad style. // * // * The caller gets a direct reference to the internal field. This is usually dangerous, // * since the Date object state can be changed both by this class and its caller. // * That is, this class is no longer in complete control of fDate. // */ // public Date getDateOfDiscovery() { // return fDateOfDiscovery; // } /** * Returns a mutable object - good style. * * Returns a defensive copy of the field. * The caller of this method can do anything they want with the * returned Date object, without affecting the internals of this * class in any way. Why? Because they do not have a reference to * fDate. Rather, they are playing with a second Date that initially has the * same data as fDate. */ public Date getDateOfDiscovery() { return new Date(fDateOfDiscovery.getTime()); } // PRIVATE // /** * Final primitive data is always immutable. */ private final double fMass; /** * An immutable object field. (String objects never change state.) */ private final String fName; /** * A mutable object field. In this case, the state of this mutable field * is to be changed only by this class. (In other cases, it makes perfect * sense to allow the state of a field to be changed outside the native * class; this is the case when a field acts as a "pointer" to an object * created elsewhere.) */ private final Date fDateOfDiscovery; } 

Inmutable = no modificable. Entonces, hacer que las propiedades sean definitivas es una buena idea. Si no todas las propiedades de un objeto están protegidas contra el cambio, no diría que el objeto es inmutable.

PERO un objeto también es inmutable si no proporciona ningún setter para sus propiedades privadas.

No.

Por ejemplo, vea la implementación de java.lang.String . Las cadenas son inmutables en Java, pero el hash campo no es definitivo (se calcula de forma perezosa la primera vez que se llama a hashCode y luego se almacena en caché). Pero esto funciona porque hash puede tomar solo un valor no predeterminado que es el mismo cada vez que se calcula.

La clase de cadena es inmutable pero el hash de propiedad no es definitivo

Bueno, es posible, pero con algunas reglas / restricciones y es que el acceso a propiedades / campos mutables debe proporcionar el mismo resultado cada vez que accedamos a él.

En el código Hash de la clase String realmente se calcula en la matriz final de caracteres que no va a cambiar si se ha construido String. Por lo tanto, la clase inmutable puede contener campos / propiedades mutables, pero debe asegurarse de que el acceso al campo / propiedad produzca el mismo resultado cada vez que se acceda.

Para responder a su pregunta, no es obligatorio tener todos los campos finales en una clase inmutable.

Para leer más, visite aquí [blog]: http://javaunturnedtopics.blogspot.in/2016/07/string-is-immutable-and-property-hash.html