É Inteiro Imutável

100

Eu sei que isso é provavelmente muito estúpido, mas muitos lugares afirmam que a classe Integer em Java é imutável, mas o seguinte código:

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

Executa sem problemas, fornecendo o resultado (esperado) 6. Portanto, efetivamente, o valor de a foi alterado. Isso não significa que Integer é mutável? Questão secundária e um pouco offtopic: "Classes imutáveis ​​não precisam de construtores de cópia". Alguém se importa em explicar por quê?

K.Steff
fonte
12
A classe é imutável, mas o autoboxing está fazendo coisas incríveis acontecerem: stackoverflow.com/questions/3085332/…
wkl
Obrigado, boxe era a palavra-chave que eu precisava para pesquisar no Google :)
K.Steff
7
Você está confundindo imutável com um valor final ou constante.
Código Entusiasta de

Respostas:

94

Imutável não significa que anunca possa ser igual a outro valor. Por exemplo, também Stringé imutável, mas ainda posso fazer isso:

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

strnão foi alterado; em vez disso, stré agora um objeto completamente instanciado, assim como o seu Integer. Portanto, o valor de anão sofreu mutação, mas foi substituído por um objeto completamente novo, isto é new Integer(6).

Travis Webb
fonte
14
"Isso ocorre porque str é agora um objeto completamente instanciado". Ou melhor, str (a varibale) aponta para um novo objeto. O objeto em si não é mutável, mas como a variável não é final, ela pode apontar para um objeto diferente.
Sandman
Sim, ele aponta para um objeto diferente que foi instanciado como resultado da +=operação.
Travis Webb
11
Estritamente falando, não precisa ser um objeto novo . O boxing usa Integer.valueOf(int)e esse método mantém um cache de Integerobjetos. Portanto, o resultado de +=em uma Integervariável pode ser um objeto que existia anteriormente (ou pode até ser o mesmo objeto ... no caso de a += 0).
Stephen C
1
Por que o JavaDoc for String diz explicitamente que é imutável, mas o JavaDoc for Integer não? É por essa diferença que estou lendo esta Questão ...
cellepo
50

aé uma "referência" a algum Integer (3), sua abreviação a+=brealmente significa fazer isto:

a = new Integer(3 + 3)

Portanto, não, os inteiros não são mutáveis, mas as variáveis ​​que apontam para eles são *.

* É possível ter variáveis ​​imutáveis, estas são denotadas pela palavra-chave final, o que significa que a referência não pode mudar.

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

Você pode determinar que o objeto mudou usando System.identityHashCode()(A melhor maneira é usar simples, ==no entanto, não é tão óbvio que a referência ao invés do valor mudou)

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

estampas

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

Você pode ver que o "id" subjacente do objeto aao qual se refere mudou.

Peter Lawrey
fonte
System.identityHashCode () é uma dica muito boa. Obrigado por isso.
Ad Infinitum
10

Para a pergunta inicial feita,

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

O número inteiro é imutável, então o que aconteceu acima é que 'a' mudou para uma nova referência de valor 6. O valor inicial 3 é deixado sem nenhuma referência na memória (ele não foi alterado), então pode ser coletado como lixo.

Se isso acontecer com uma String, ela se manterá no pool (no espaço PermGen) por um período maior do que os Inteiros, pois espera ter referências.

Harsha
fonte
8

Sim, o inteiro é imutável.

A é uma referência que aponta para um objeto. Quando você executa a + = 3, isso reatribui A para fazer referência a um novo objeto Integer, com um valor diferente.

Você nunca modificou o objeto original, em vez disso, apontou a referência para um objeto diferente.

Leia sobre a diferença entre objetos e referências aqui .

Andy
fonte
Dito de maneira simples e fácil em uma linguagem leiga, com todas as outras explicações complexas por aí :)
Roshan Fernando
5

Imutável não significa que você não pode alterar o valor de uma variável. Significa apenas que qualquer nova atribuição cria um novo objeto (atribui a ele um novo local de memória) e, em seguida, o valor é atribuído a ele.

Para entender isso por si mesmo, execute a atribuição de Inteiro em um loop (com o inteiro declarado fora do loop) e observe os objetos ativos na memória.

A razão pela qual o construtor de cópia não é necessário para objetos imutáveis ​​é o simples bom senso. Como cada atribuição cria um novo objeto, a linguagem tecnicamente já cria uma cópia, portanto, você não precisa criar outra cópia.

uncaught_exceptions
fonte
2

"Classes imutáveis ​​não precisam de construtores de cópia". Alguém se importa em explicar por quê?

A razão é que raramente há necessidade de copiar (ou mesmo qualquer ponto na cópia) uma instância de uma classe imutável. A cópia do objeto deve ser "igual" ao original e, se for o mesmo, não haverá necessidade de criá-lo.

No entanto, existem algumas suposições subjacentes:

  • Ele pressupõe que seu aplicativo não atribui nenhum significado à identidade do objeto das instâncias da classe.

  • Ele assume que a classe está sobrecarregada equalse hashCodeque uma cópia de uma instância seria "igual" ao original ... de acordo com esses métodos.

Uma ou ambas as suposições podem ser falsas e isso pode justificar a adição de um construtor de cópia.

Stephen C
fonte
1

É assim que eu entendo imutável

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 pudesse sofrer mutação, "a" imprimiria 8, mas não, porque é imutável, é por isso que é 3. Seu exemplo é apenas uma nova atribuição.

Ndheti
fonte
0

Posso deixar claro que Integer (e outras crenças como Float, Short etc) são imutáveis ​​por um código de amostra simples:

Código de amostra

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 atual

O resultado chega a ele Hi There 100 em vez do resultado esperado (no caso de sb e i serem objetos mutáveis) Hi There 1000

Isso mostra que o objeto criado por i em principal não foi modificado, enquanto o sb foi modificado.

Portanto, StringBuilder demonstrou comportamento mutável, mas não Integer.

Portanto, o inteiro é imutável. Provado Conseqüentemente

Outro código sem apenas inteiro:

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
fonte
Você está fazendo duas coisas diferentes - tentando reatribuir o inteiro e chamando um método no stringbuilder. Se você fizer isso, private void doStringBuilder(StringBuilder sb){ sb = new StringBuilder(); }então sbnão será alterado.
MT0
Eu adicionei StringBuilder (que é mutável) para justapor Integer com outro objeto que é mutável. Se você quiser, pode excluir todo o código relacionado ao StringBuilder e imprimir apenas para ver 100.
Ashutosh Nigam
Isso não prova imutabilidade - tudo o que você está fazendo é refazer o hash deste exemplo, demonstrando que o Java usa passagem por valor (e que os valores passados ​​para objetos são ponteiros).
MT0
Experimenteprivate void doInteger(Integer i){ System.out.println( i == 100 ); i=1000; System.out.println( i == 100 ); }
MT0
@ MT0 Quando você passa por valor, StringBuilder ainda aponta para o mesmo objeto, mas Integer está passando uma nova cópia sem referência ao mesmo objeto. Se você imprimir no doInteger, estará exibindo a cópia que possui a função, não a função principal. Queremos ver se o objeto apontado por i em main é o mesmo ou não. Espero que esclareça os conceitos :) A versão imutável de StringBuilder também é String. Deixe-me saber se você quiser que eu compartilhe uma amostra para ele.
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
}

O resultado é:

Hi Bye 1000 5000 1000 5000 d a

Então char é mutável, String Integer e int são imutáveis.

özgür Arslan
fonte
1
Esta resposta não fornece nenhuma informação dos outros.
Giulio Caccin,