Est un entier immuable

102

Je sais que c'est probablement très stupide, mais de nombreux endroits affirment que la classe Integer en Java est immuable, mais le code suivant:

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

S'exécute sans problème en donnant le résultat (attendu) 6. Donc effectivement la valeur de a a changé. Cela ne signifie-t-il pas qu'Integer est mutable? Question secondaire et un peu hors-sujet: "Les classes immuables n'ont pas besoin de constructeurs de copie". Quelqu'un veut-il expliquer pourquoi?

K.Steff
la source
12
La classe est immuable, mais l'auto-boxe fait bouger les choses: stackoverflow.com/questions/3085332/…
wkl
Merci, la boxe était le mot-clé dont j'avais besoin pour google :)
K.Steff
7
Vous confondez immuable avec une valeur finale ou constante.
Code Enthusiastic

Réponses:

95

Immuable ne signifie pas que cela ane peut jamais égaler une autre valeur. Par exemple, Stringest immuable aussi, mais je peux toujours le faire:

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

str n'a pas été changé, plutôt str s'agit un objet complètement nouvellement instancié, tout comme le vôtre Integer. Donc, la valeur de an'a pas muté, mais elle a été remplacée par un objet complètement nouveau, ie new Integer(6).

Travis Webb
la source
14
"C'est parce que str est maintenant un objet complètement nouvellement instancié". Ou plutôt str (une varibale) pointe vers un nouvel objet. L'objet lui-même n'est pas modifiable, mais comme la variable n'est pas définitive, elle peut pointer vers un objet différent.
Sandman
Oui, il pointe vers un objet différent qui a été instancié à la suite de l' +=opération.
Travis Webb
11
À proprement parler, il n'est pas nécessaire que ce soit un nouvel objet. La boxe utilise Integer.valueOf(int)et cette méthode maintient un cache d' Integerobjets. Ainsi, le résultat de +=sur une Integervariable pourrait être un objet qui existait auparavant (ou il pourrait même être le même objet ... dans le cas de a += 0).
Stephen C
1
Pourquoi le JavaDoc for String dit-il explicitement qu'il est immuable, mais pas le JavaDoc for Integer? Cette différence est la raison pour laquelle je lis cette Question ...
cellepo
51

aest une "référence" à un nombre entier (3), votre raccourci a+=bsignifie vraiment faire ceci:

a = new Integer(3 + 3)

Donc non, les entiers ne sont pas modifiables, mais les variables qui pointent vers eux sont *.

* Il est possible d'avoir des variables immuables, celles-ci sont désignées par le mot-clé final, ce qui signifie que la référence ne peut pas changer.

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

Vous pouvez déterminer que l'objet a changé en utilisant System.identityHashCode()(Une meilleure façon est d'utiliser plain, ==mais ce n'est pas aussi évident que la référence plutôt que la valeur a changé)

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

impressions

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

Vous pouvez voir que l '«id» sous-jacent de l'objet auquel afait référence a changé.

Peter Lawrey
la source
1
System.identityHashCode () est une très bonne astuce. Merci pour cela.
Ad Infinitum
11

À la question initiale posée,

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

L'entier est immuable, donc ce qui s'est passé ci-dessus est que «a» a été changé en une nouvelle référence de valeur 6. La valeur initiale 3 est laissée sans référence dans la mémoire (elle n'a pas été modifiée), elle peut donc être récupérée.

Si cela arrive à une chaîne, elle restera dans le pool (dans l'espace PermGen) pendant une période plus longue que les entiers car elle s'attend à avoir des références.

Harsha
la source
8

Oui Integer est immuable.

A est une référence qui pointe vers un objet. Lorsque vous exécutez un + = 3, cela réaffecte A pour référencer un nouvel objet Integer, avec une valeur différente.

Vous n'avez jamais modifié l'objet d'origine, vous avez plutôt pointé la référence vers un objet différent.

Découvrez la différence entre les objets et les références ici .

Andy
la source
Simplement et facilement dit dans la langue d'un profane, avec toutes les autres explications complexes là-bas :)
Roshan Fernando
5

Immuable ne signifie pas que vous ne pouvez pas modifier la valeur d'une variable. Cela signifie simplement que toute nouvelle affectation crée un nouvel objet (lui attribue un nouvel emplacement mémoire), puis la valeur lui est affectée.

Pour comprendre cela par vous-même, effectuez une affectation entière dans une boucle (avec un entier déclaré en dehors de la boucle) et regardez les objets en direct en mémoire.

La raison pour laquelle le constructeur de copie n'est pas nécessaire pour les objets immuables est le simple bon sens. Étant donné que chaque affectation crée un nouvel objet, la langue crée déjà techniquement une copie, vous n'avez donc pas à créer une autre copie.

uncaught_exceptions
la source
2

"Les classes immuables n'ont pas besoin de constructeurs de copie". Quelqu'un veut-il expliquer pourquoi?

La raison en est qu'il est rarement nécessaire de copier (ou même de copier) une instance d'une classe immuable. La copie de l'objet doit être "la même que" l'original, et si elle est la même, il ne devrait pas être nécessaire de la créer.

Il y a cependant quelques hypothèses sous-jacentes:

  • Cela suppose que votre application ne donne aucune signification à l'identité d'objet des instances de la classe.

  • Il suppose que la classe a été surchargée equalset hashCodequ'une copie d'une instance serait "identique à" l'original ... selon ces méthodes.

L'une ou l'autre de ces hypothèses ou les deux pourraient être fausses, ce qui pourrait justifier l'ajout d'un constructeur de copie.

Stephen C
la source
1

C'est comme ça que je comprends immuable

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 pouvait muter, "a" afficherait 8 mais ce n'est pas le cas car il est immuable, c'est pourquoi il est 3. Votre exemple est juste une nouvelle affectation.

Ndheti
la source
0

Je peux préciser que Integer (et d'autres de ses croyances comme Float, Short, etc.) sont immuables par un simple exemple de code:

Exemple de code

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

}

Résultat actuel

Le résultat arrive au Hi There 100 au lieu du résultat attendu (dans le cas où sb et i sont des objets mutables) Hi There 1000

Cela montre que l'objet créé par i dans main n'est pas modifié, alors que le sb est modifié.

Ainsi, StringBuilder a démontré un comportement modifiable mais pas Integer.

Donc Integer est immuable. Par conséquent prouvé

Un autre code sans seulement 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
la source
Vous faites deux choses différentes: essayer de réaffecter l'entier et appeler une méthode sur le constructeur de chaînes. Si vous le faites private void doStringBuilder(StringBuilder sb){ sb = new StringBuilder(); }alors sbest inchangée.
MT0
J'ai ajouté StringBuilder (qui est mutable) pour juxtaposer simplement Integer avec un autre objet mutable. Si vous voulez, vous pouvez supprimer tout le code lié à StringBuilder et simplement imprimer i pour voir 100.
Ashutosh Nigam
Cela ne prouve pas l'immuabilité - tout ce que vous faites est de re-hacher cet exemple pour démontrer que Java utilise le passage par valeur (et que les valeurs transmises pour les objets sont des pointeurs).
MT0
Essayezprivate void doInteger(Integer i){ System.out.println( i == 100 ); i=1000; System.out.println( i == 100 ); }
MT0
@ MT0 Lorsque vous passez par valeur, StringBuilder pointe toujours vers le même objet mais Integer transmet une nouvelle copie et non une référence au même objet. Si vous imprimez dans doInteger, vous affichez la copie possédée par la fonction et non la fonction principale. Nous voulons voir si l'objet pointé par i dans main est le même ou non. J'espère que cela efface les concepts :) Aussi la version immuable de StringBuilder est String. Faites-moi savoir si vous souhaitez que je partage un échantillon pour cela.
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 sortie est:

Salut au revoir 1000 5000 1000 5000 d a

Donc char est mutable, String Integer et int sont immuables.

özgür arslan
la source
1
Cette réponse ne fournit aucune information des autres.
Giulio Caccin