È intero immutabile


102

So che questo è probabilmente molto stupido, ma molti posti affermano che la classe Integer in Java è immutabile, tuttavia il codice seguente:

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

Viene eseguito senza problemi dando il risultato (atteso) 6. Così effettivamente il valore di a è cambiato. Non significa che Integer è mutabile? Domanda secondaria e un po 'fuori tema: "Le classi immutabili non hanno bisogno di costruttori di copia". Qualcuno vuole spiegare perché?


12
La classe è immutabile, ma autoboxing sta facendo cose accadono funky: stackoverflow.com/questions/3085332/...
WKL

Grazie, boxe era la parola chiave di cui avevo bisogno per google :)
K.Steff

7
Stai confondendo immutabile con un valore finale o costante.
Code Enthusiastic

Risposte:


95

Immutabile non significa che anon possa mai eguagliare un altro valore. Ad esempio, Stringè anche immutabile, ma posso ancora farlo:

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

strnon è stato cambiato, piuttosto strè ora un oggetto completamente nuovo istanziato, proprio come lo Integerè il tuo . Quindi il valore di anon è mutato, ma è stato sostituito con un oggetto completamente nuovo, ad es new Integer(6).


14
"Questo perché str è ora un oggetto completamente nuovo istanziato". O meglio, str (un varibale) punta a un nuovo oggetto. L'oggetto in sé non è modificabile, ma poiché la variabile non è finale, può puntare a un oggetto diverso.
Sandman

Sì, punta a un oggetto diverso di cui è stata creata un'istanza come risultato +=dell'operazione.
Travis Webb

11
A rigor di termini, non è necessario che sia un oggetto nuovo . La boxe usa Integer.valueOf(int)e quel metodo mantiene una cache di Integeroggetti. Quindi il risultato di +=su una Integervariabile potrebbe essere un oggetto che esisteva in precedenza (o potrebbe anche essere lo stesso oggetto ... nel caso di a += 0).
Stephen C

1
Perché JavaDoc per String dice esplicitamente che è immutabile, ma JavaDoc per Integer no? Questa differenza è il motivo per cui sto leggendo questa domanda ...
cellepo

52

aè un "riferimento" a un numero intero (3), la tua scorciatoia a+=bsignifica davvero fare questo:

a = new Integer(3 + 3)

Quindi no, i numeri interi non sono modificabili, ma le variabili che puntano a loro sono *.

* È possibile avere variabili immutabili, queste sono indicate dalla parola chiave final, il che significa che il riferimento potrebbe non cambiare.

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

20

Puoi determinare che l'oggetto è cambiato usando System.identityHashCode()(Un modo migliore è usare plain, ==ma non è così ovvio che il riferimento piuttosto che il valore sia cambiato)

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

stampe

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

Puoi vedere che l '"id" sottostante dell'oggetto a cui afa riferimento è cambiato.


1
System.identityHashCode () è un ottimo suggerimento. Grazie per questo.
Ad Infinitum

11

Alla domanda iniziale posta,

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

Integer è immutabile, quindi ciò che è successo sopra è che "a" è stato modificato in un nuovo riferimento di valore 6. Il valore iniziale 3 non viene lasciato alcun riferimento in memoria (non è stato modificato), quindi può essere sottoposto a garbage collection.

Se ciò accade a una stringa, rimarrà nel pool (nello spazio PermGen) per un periodo più lungo rispetto agli interi poiché si aspetta di avere riferimenti.


8

Sì Integer è immutabile.

A è un riferimento che punta a un oggetto. Quando esegui un + = 3, questo riassegna A per fare riferimento a un nuovo oggetto Integer, con un valore diverso.

Non hai mai modificato l'oggetto originale, piuttosto hai puntato il riferimento a un oggetto diverso.

Leggi la differenza tra oggetti e riferimenti qui .


Detto in modo semplice e facile in un linguaggio laico, con tutte le altre spiegazioni complesse là fuori :)
Roshan Fernando

5

Immutabile non significa che non è possibile modificare il valore per una variabile. Significa solo che ogni nuova assegnazione crea un nuovo oggetto (gli assegna una nuova posizione di memoria) e quindi il valore gli viene assegnato.

Per capirlo da soli, eseguire l'assegnazione di numeri interi in un ciclo (con un numero intero dichiarato al di fuori del ciclo) e guardare gli oggetti attivi in ​​memoria.

Il motivo per cui il costruttore di copie non è necessario per gli oggetti immutabili è il semplice buon senso. Poiché ogni assegnazione crea un nuovo oggetto, la lingua tecnicamente crea già una copia, quindi non è necessario crearne un'altra.


2

"Le classi immutabili non necessitano di costruttori di copia". Qualcuno vuole spiegare perché?

Il motivo è che raramente c'è bisogno di copiare (o anche qualsiasi punto nella copia) un'istanza di una classe immutabile. La copia dell'oggetto dovrebbe essere "uguale" all'originale e, se è lo stesso, non dovrebbe essere necessario crearlo.

Tuttavia, ci sono alcune ipotesi di fondo:

  • Si presume che l'applicazione non attribuisca alcun significato all'identità dell'oggetto delle istanze della classe.

  • Si presume che la classe sia stata sovraccaricata equalse hashCodequindi che una copia di un'istanza sarebbe "uguale" all'originale ... secondo questi metodi.

Una o entrambe queste ipotesi potrebbero essere false e ciò potrebbe giustificare l'aggiunta di un costruttore di copia.


1

Questo è il modo in cui intendo immutabile

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

Se int potesse mutare, "a" stamperebbe 8 ma non lo fa perché è immutabile, ecco perché è 3. Il tuo esempio è solo un nuovo incarico.


0

Posso chiarire che Integer (e altri suoi credo come Float, Short ecc.) Sono immutabili con un semplice codice di esempio:

Codice di esempio

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

}

Risultato attuale

Il risultato arriva a Hi There 100 invece del risultato atteso (nel caso in cui sia sb che io siano oggetti mutabili) Hi There 1000

Questo mostra che l'oggetto creato da i in main non è modificato, mentre sb è modificato.

Quindi StringBuilder ha dimostrato un comportamento mutevole ma non Integer.

Quindi Integer è immutabile. Quindi dimostrato

Un altro codice senza 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;
    }


}

Stai facendo due cose diverse: provando a riassegnare il numero intero e chiamando un metodo sullo stringbuilder. Se lo fai private void doStringBuilder(StringBuilder sb){ sb = new StringBuilder(); }, allora sbè invariato.
MT0

Ho aggiunto StringBuilder (che è mutabile) per giustapporre Integer con un altro oggetto mutabile. Se vuoi puoi cancellare tutto il codice relativo a StringBuilder e stamparlo per vedere 100.
Ashutosh Nigam

Ciò non prova l'immutabilità: tutto ciò che stai facendo è ripetere l'hash di questo esempio dimostrando che Java utilizza il passaggio per valore (e che i valori passati per gli oggetti sono puntatori).
MT0

Provaprivate void doInteger(Integer i){ System.out.println( i == 100 ); i=1000; System.out.println( i == 100 ); }
MT0

@ MT0 Quando si passa per valore, StringBuilder punta ancora allo stesso oggetto ma Integer sta passando una nuova copia che non fa riferimento allo stesso oggetto. Se stampi in doInteger, stai visualizzando la copia posseduta dalla funzione e non la funzione principale. Vogliamo vedere se l'oggetto puntato da i in main è lo stesso oppure no. Spero che chiarisca i concetti :) Anche la versione immutabile di StringBuilder è String. Fammi sapere se vuoi che condivida un campione per questo.
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
}

L'output è:

Ciao ciao 1000 5000 1000 5000 d a

Quindi char è mutabile, String Integer e int sono immutabili.


1
Questa risposta non fornisce alcuna informazione dagli altri.
Giulio Caccin
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.