2010-03-12 11 views
60

me encontré con el siguiente código en una base de código que estoy trabajando en:¿Por qué se puede modificar el objeto final?

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); 
    } 

    ... 

INSTANCIA se declara como final. ¿Por qué se pueden agregar objetos a INSTANCE? No debería eso invalidar el uso de final. (No es así)

Supongo que la respuesta tiene que ver con los punteros y la memoria, pero me gustaría saberlo con certeza.

+0

Este concepto erróneo aparece con bastante frecuencia, no necesariamente como una pregunta. A menudo como una respuesta o comentario. – Robin

+4

Explicación simple de JLS: ** "Si una variable final contiene una referencia a un objeto, entonces el estado del objeto puede cambiarse mediante operaciones en el objeto, pero la variable siempre se referirá al mismo objeto." ** [Documentación de JLS] (http://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.12.4) – realPK

Respuesta

122

'final' simplemente hace el objeto referencia inalterable. El objeto al que apunta no es inmutable al hacer esto. INSTANCE nunca puede referirse a otro objeto, pero el objeto al que se refiere puede cambiar de estado.

+1

+1, para obtener más información, consulte http: //java.sun.com/docs/books/jls/second_edition/html/typesValues.doc.html, sección 4.5.4. –

+0

Digamos que deserializo un objeto 'ConfigurationService' y trato de hacer una INSTANCIA =' deserializedConfigurationService' no sería permitido? – diegoaguilar

+0

Nunca podría asignar 'INSTANCE' para referirse a otro objeto. No importa de dónde vino el otro objeto. (NB hay una 'INSTANCE' por' ClassLoader' que ha cargado esta clase. En teoría, puedes cargar la clase varias veces en una JVM y cada una está separada. Pero este es un punto técnico diferente). –

3

Final e inmutable no son lo mismo. Final: es la referencia no puede ser reasignado por lo que no se puede decir

INSTANCE = ... 

Inmutable significa que el objeto en sí no puede ser modificado. Un ejemplo de esto es la clase java.lang.String. No puedes modificar el valor de una cadena.

20

La clave del malentendido está en el título de su pregunta. No es el objeto que es final, es la variable . El valor de la variable no puede cambiar, pero los datos que contiene pueden.

Recuerde siempre que cuando declara una variable de tipo de referencia, el valor de esa variable es una referencia, no un objeto.

10

final solo significa que la referencia no se puede cambiar. No puede reasignar INSTANCE a otra referencia si se declara como final. El estado interno del objeto sigue siendo mutable.

final ConfigurationService INSTANCE = new ConfigurationService(); 
ConfigurationService anotherInstance = new ConfigurationService(); 
INSTANCE = anotherInstance; 

lanzaría un error de compilación

6

Una vez que se ha asignado una variable final, que siempre contiene el mismo valor. Si una variable final contiene una referencia a un objeto, entonces el estado del objeto puede modificarse mediante operaciones en el objeto, pero la variable siempre se referirá al mismo objeto. Esto se aplica también a las matrices, porque las matrices son objetos; si una variable final contiene una referencia a una matriz, los componentes de la matriz pueden cambiarse por las operaciones en la matriz, pero la variable siempre se referirá a la misma matriz.

Source

He aquí una guía sobre making an object immutable.

30

Ser final no es lo mismo que ser inmutable.

final != immutable

La palabra clave final se utiliza para asegurarse de que la referencia no se cambia (es decir, la referencia que se no puede ser sustituido por uno nuevo)

Pero, si el atributo es el yo es modificable, está bien hacer lo que acabas de describir.

Por ejemplo

class SomeHighLevelClass { 
    public final MutableObject someFinalObject = new MutableObject(); 
} 

Si creamos una instancia de esta clase, no seremos capaces de asignar otro valor para el atributo someFinalObject porque es última.

Así que 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í es mutable como esto:

class MutableObject { 
    private int n = 0; 

    public void incrementNumber() { 
     n++; 
    } 
    public String toString(){ 
     return ""+n; 
    } 
} 

Entonces, el valor contenido por ese objeto mutable se pueden cambiar.

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 su mensaje:

public static void addProvider(ConfigurationProvider provider) { 
    INSTANCE.providers.add(provider); 
} 

Aquí no va a cambiar el valor de ejemplo, su están modificando su estado interno (vía, método providers.add)

si desea evitar que la definición de la clase debe ser cambiado como esto:

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, podría no tener mucho sentido :)

Por cierto, también tiene que synchronize access to it básicamente por la misma razón.

2

Java no tiene el concepto de inmutabilidad incorporado en el lenguaje. No hay forma de marcar los métodos como un mutador. Por lo tanto, el lenguaje no tiene forma de imponer la inmutabilidad del objeto.

1

Si está utilizando la palabra clave final para el objeto, hashCode nunca cambiará por lo que es inmutable. Se usará en el patrón singleton.

class mysingleton1{ 

private mysingleton1(){} 
public static final mysingleton1 instance= new mysingleton1(); 

    public static mysingleton1 getInstance(){ 

     return instance; 

    } 

} 
Cuestiones relacionadas