Es entero inmutable

101

Sé que esto probablemente sea muy estúpido, pero muchos lugares afirman que la clase Integer en Java es inmutable, pero el siguiente código:

Integer a=3;
Integer b=3;
a+=b;
System.out.println(a);

Se ejecuta sin ningún problema para dar el resultado (esperado) 6. Así que efectivamente el valor de a ha cambiado. ¿No significa eso que Integer es mutable? Pregunta secundaria y un poco fuera de tema: "Las clases inmutables no necesitan constructores de copia". ¿Alguien quiere explicar por qué?

K.Steff
fuente
12
La clase es inmutable, pero el autoboxing está haciendo que sucedan cosas
raras
Gracias, boxeo era la palabra clave que necesitaba para google :)
K.Steff
7
Estás confundiendo inmutable con un valor final o constante.
Code Enthusiastic

Respuestas:

95

Inmutable no significa que anunca pueda igualar otro valor. Por ejemplo, también Stringes inmutable, pero aún puedo hacer esto:

String str = "hello";
// str equals "hello"
str = str + "world";
// now str equals "helloworld"

strno se modificó, sino strque ahora es un objeto instanciado completamente nuevo, al igual que el suyo Integer. Entonces, el valor de ano cambió, pero fue reemplazado por un objeto completamente nuevo, es decir new Integer(6).

Travis Webb
fuente
14
"Esto se debe a que str es ahora un objeto instanciado completamente nuevo". O, mejor dicho, str (a varibale) apunta a un nuevo objeto. El objeto en sí no es mutable, pero como la variable no es final, puede apuntar a un objeto diferente.
Sandman
Sí, apunta a un objeto diferente que fue instanciado como resultado de la +=operación.
Travis Webb
11
Estrictamente hablando, no es necesario que sea ​​un objeto nuevo . El boxeo utiliza Integer.valueOf(int)y ese método mantiene un caché de Integerobjetos. Entonces, el resultado de +=una Integervariable podría ser un objeto que existía anteriormente (o incluso podría ser el mismo objeto ... en el caso de a += 0).
Stephen C
1
¿Por qué JavaDoc para String dice explícitamente que es inmutable, pero JavaDoc para Integer no? Esa diferencia es la razón por la que estoy leyendo esta pregunta ...
cellepo
51

aes una "referencia" a algún Integer (3), su taquigrafía a+=brealmente significa hacer esto:

a = new Integer(3 + 3)

Entonces no, los enteros no son mutables, pero las variables que los señalan son *.

* Es posible tener variables inmutables, estas se denotan con la palabra clave final, lo que significa que la referencia no puede cambiar.

final Integer a = 3;
final Integer b = 3;
a += b; // compile error, the variable `a` is immutable, too.
Mark Elliot
fuente
19

Puede determinar que el objeto ha cambiado usando System.identityHashCode()(una mejor manera es usar simple, ==sin embargo, no es tan obvio que la referencia en lugar del valor haya cambiado)

Integer a = 3;
System.out.println("before a +=3; a="+a+" id="+Integer.toHexString(System.identityHashCode(a)));
a += 3;
System.out.println("after a +=3; a="+a+" id="+Integer.toHexString(System.identityHashCode(a)));

huellas dactilares

before a +=3; a=3 id=70f9f9d8
after a +=3; a=6 id=2b820dda

Puede ver que la "identificación" subyacente del objeto al que se arefiere ha cambiado.

Peter Lawrey
fuente
1
System.identityHashCode () es un muy buen consejo. Gracias por esto.
Ad Infinitum
11

A la pregunta inicial formulada,

Integer a=3;
Integer b=3;
a+=b;
System.out.println(a);

El entero es inmutable, por lo que lo que sucedió arriba es que 'a' ha cambiado a una nueva referencia de valor 6. El valor inicial 3 se deja sin referencia en la memoria (no se ha cambiado), por lo que se puede recolectar basura.

Si esto le sucede a una cadena, se mantendrá en el grupo (en el espacio de PermGen) durante un período más largo que los enteros, ya que espera tener referencias.

Harsha
fuente
8

Sí Integer es inmutable.

A es una referencia que apunta a un objeto. Cuando ejecuta un + = 3, eso reasigna A para hacer referencia a un nuevo objeto Integer, con un valor diferente.

Nunca modificó el objeto original, sino que apuntó la referencia a un objeto diferente.

Lea sobre la diferencia entre objetos y referencias aquí .

Andy
fuente
Dicho de forma simple y sencilla en un lenguaje común, con todas las demás explicaciones complejas por ahí :)
Roshan Fernando
5

Inmutable no significa que no pueda cambiar el valor de una variable. Simplemente significa que cualquier nueva asignación crea un nuevo objeto (le asigna una nueva ubicación de memoria) y luego se le asigna el valor.

Para comprender esto por sí mismo, realice la asignación de números enteros en un bucle (con el número entero declarado fuera del bucle) y observe los objetos activos en la memoria.

La razón por la que el constructor de copias no es necesario para objetos inmutables es el simple sentido común. Dado que cada asignación crea un nuevo objeto, el lenguaje técnicamente ya crea una copia, por lo que no tiene que crear otra copia.

excepciones_uncaught
fuente
2

"Las clases inmutables no necesitan constructores de copia". ¿Alguien quiere explicar por qué?

La razón es que rara vez hay necesidad de copiar (o incluso de copiar) una instancia de una clase inmutable. La copia del objeto debe ser "igual" que el original, y si es el mismo, no debería ser necesario crearlo.

Sin embargo, existen algunas suposiciones subyacentes:

  • Asume que su aplicación no da ningún significado a la identidad de objeto de las instancias de la clase.

  • Se asume que la clase se ha sobrecargado equalsy hashCodeque una copia de una instancia sería "igual que" el original ... de acuerdo con estos métodos.

Cualquiera de esas suposiciones o ambas podrían ser falsas, y eso podría justificar la adición de un constructor de copias.

Stephen C
fuente
1

Así es como entiendo inmutable

int a=3;    
int b=a;
b=b+5;
System.out.println(a); //this returns 3
System.out.println(b); //this returns 8

Si int pudiera mutar, "a" imprimiría 8 pero no lo hace porque es inmutable, por eso es 3. Su ejemplo es solo una nueva asignación.

Ndheti
fuente
0

Puedo dejar en claro que Integer (y otros de su credo como Float, Short, etc.) son inmutables mediante un código de muestra simple:

Código de muestra

public class Test{
    public static void main(String... args){
        Integer i = 100;
        StringBuilder sb = new StringBuilder("Hi");
        Test c = new Test();
        c.doInteger(i);
        c.doStringBuilder(sb);
        System.out.println(sb.append(i)); //Expected result if Integer is mutable is Hi there 1000
    }

    private void doInteger(Integer i){
        i=1000;
    }

    private void doStringBuilder(StringBuilder sb){
        sb.append(" there");
    }

}

Resultado actual

El resultado llega a Hi There 100 en lugar del resultado esperado (en caso de que sb e i sean objetos mutables) Hi There 1000

Esto muestra que el objeto creado por i en main no está modificado, mientras que el sb está modificado.

Entonces StringBuilder demostró un comportamiento mutable pero no Integer.

Entonces Integer es inmutable. Por lo tanto probado

Otro código sin solo Integer:

public class Test{
    public static void main(String... args){
        Integer i = 100;
        Test c = new Test();
        c.doInteger(i);
        System.out.println(i); //Expected result is 1000 in case Integer is mutable
    }

    private void doInteger(Integer i){
        i=1000;
    }


}
Ashutosh Nigam
fuente
Está haciendo dos cosas diferentes: intentar reasignar el entero y llamar a un método en el generador de cadenas. Si lo hace private void doStringBuilder(StringBuilder sb){ sb = new StringBuilder(); }a continuación, sbno se modifica.
MT0
Agregué StringBuilder (que es mutable) para yuxtaponer Integer con otro objeto que es mutable. Si lo desea, puede eliminar todo el código relacionado con StringBuilder y simplemente imprimir i para ver 100.
Ashutosh Nigam
Esto no prueba la inmutabilidad; todo lo que está haciendo es volver a aplicar el hash de este ejemplo para demostrar que Java usa el paso por valor (y que los valores pasados ​​para los objetos son punteros).
MT0
Probarprivate void doInteger(Integer i){ System.out.println( i == 100 ); i=1000; System.out.println( i == 100 ); }
MT0
@ MT0 Cuando pasa por valor, StringBuilder sigue apuntando al mismo objeto pero Integer está pasando una nueva copia, no una referencia al mismo objeto. Si imprime en doInteger, está mostrando la copia que posee la función, no la función principal. Queremos ver si el objeto señalado por i en main es el mismo o no. Espero que aclare los conceptos :) También la versión inmutable de StringBuilder es String. Avísame si quieres que te comparta una muestra.
Ashutosh Nigam
-1
public static void main(String[] args) {
    // TODO Auto-generated method stub

    String s1="Hi";
    String s2=s1;

    s1="Bye";

    System.out.println(s2); //Hi  (if String was mutable output would be: Bye)
    System.out.println(s1); //Bye

    Integer i=1000;
    Integer i2=i;

    i=5000;

    System.out.println(i2); // 1000
    System.out.println(i); // 5000

    int j=1000;
    int j2=j;

    j=5000;

    System.out.println(j2); // 1000
    System.out.println(j); //  5000


    char c='a';
    char b=c;

    c='d';

    System.out.println(c); // d
    System.out.println(b); // a
}

La salida es:

Hola adiós 1000 5000 1000 5000 d a

Entonces char es mutable, String Integer e int son inmutables.

özgür arslan
fuente
1
Esta respuesta no proporciona ninguna información de las demás.
Giulio Caccin