Encontré el siguiente código en una base de código en la que estoy trabajando:
public final class ConfigurationService {
private static final ConfigurationService INSTANCE = new ConfigurationService();
private List providers;
private ConfigurationService() {
providers = new ArrayList();
}
public static void addProvider(ConfigurationProvider provider) {
INSTANCE.providers.add(provider);
}
...
INSTANCE
se declara como final
. ¿Por qué se pueden agregar objetos INSTANCE
? ¿No debería invalidar el uso de final. (No lo hace).
Supongo que la respuesta tiene que ver con los punteros y la memoria, pero me gustaría saberlo con certeza.
Respuestas:
final
simplemente hace que la referencia del objeto sea invariable. El objeto al que apunta no es inmutable al hacer esto.INSTANCE
nunca puede hacer referencia a otro objeto, pero el objeto al que se refiere puede cambiar de estado.fuente
ConfigurationService
objeto e intento hacer un INSTANCE =deserializedConfigurationService
¿no estaría permitido?INSTANCE
una referencia a otro objeto. No importa de dónde venga el otro objeto. (Nota: hay unoINSTANCE
por cadaClassLoader
que ha cargado esta clase. En teoría, podría cargar la clase varias veces en una JVM y cada una está separada. Pero este es un punto técnico diferente).Ser definitivo no es lo mismo que ser inmutable.
final != immutable
La
final
palabra clave se usa para asegurarse de que la referencia no se cambie (es decir, la referencia que tiene no se puede sustituir por una nueva)Pero, si el atributo es self es modificable, está bien hacer lo que acaba de describir.
Por ejemplo
class SomeHighLevelClass { public final MutableObject someFinalObject = new MutableObject(); }
Si instanciamos esta clase, no podremos asignar otro valor al atributo
someFinalObject
porque es final .Entonces esto no es posible:
.... SomeHighLevelClass someObject = new SomeHighLevelClass(); MutableObject impostor = new MutableObject(); someObject.someFinal = impostor; // not allowed because someFinal is .. well final
Pero si el objeto en sí mismo es mutable así:
class MutableObject { private int n = 0; public void incrementNumber() { n++; } public String toString(){ return ""+n; } }
Entonces, el valor contenido por ese objeto mutable puede cambiarse.
SomeHighLevelClass someObject = new SomeHighLevelClass(); someObject.someFinal.incrementNumber(); someObject.someFinal.incrementNumber(); someObject.someFinal.incrementNumber(); System.out.println( someObject.someFinal ); // prints 3
Esto tiene el mismo efecto que tu publicación:
public static void addProvider(ConfigurationProvider provider) { INSTANCE.providers.add(provider); }
Aquí no está cambiando el valor de INSTANCE, está modificando su estado interno (a través del método Suppliers.add)
si desea evitar que la definición de la clase deba cambiarse así:
public final class ConfigurationService { private static final ConfigurationService INSTANCE = new ConfigurationService(); private List providers; private ConfigurationService() { providers = new ArrayList(); } // Avoid modifications //public static void addProvider(ConfigurationProvider provider) { // INSTANCE.providers.add(provider); //} // No mutators allowed anymore :) ....
Pero, puede que no tenga mucho sentido :)
Por cierto, también tienes que sincronizar el acceso básicamente por la misma razón.
fuente
La clave del malentendido está en el título de su pregunta. No es el objeto lo que es final, es la variable . El valor de la variable no puede cambiar, pero los datos que contiene sí pueden.
Recuerde siempre que cuando declara una variable de tipo de referencia, el valor de esa variable es una referencia, no un objeto.
fuente
final solo significa que la referencia no se puede cambiar. No puede reasignar INSTANCE a otra referencia si está declarada como final. El estado interno del objeto sigue siendo mutable.
final ConfigurationService INSTANCE = new ConfigurationService(); ConfigurationService anotherInstance = new ConfigurationService(); INSTANCE = anotherInstance;
arrojaría un error de compilación
fuente
Fuente
Aquí hay una guía sobre cómo hacer que un objeto sea inmutable .
fuente
Final e inmutable no son lo mismo. Final significa que la referencia no se puede reasignar, por lo que no se puede decir
Inmutable significa que el objeto en sí no se puede modificar. Un ejemplo de esto es la
java.lang.String
clase. No puede modificar el valor de una cadena.fuente
Java no tiene el concepto de inmutabilidad integrado en el lenguaje. No hay forma de marcar métodos como mutantes. Por lo tanto, el lenguaje no tiene forma de imponer la inmutabilidad del objeto.
fuente