Como posso passar uma class Integer corretamente por referência?

Espero que alguém possa esclarecer o que está acontecendo aqui para mim. Eu procurei na class integer por um pouco, mas como o inteiro está sobrescrevendo o operador + , não consegui descobrir o que estava errado. Meu problema é com esta linha:

 Integer i = 0; i = i + 1; // ← I think that this is somehow creating a new object! 

Aqui está o meu raciocínio: Eu sei que java é passar por valor ( ou passar por valor de referência ), então eu acho que no exemplo a seguir o object inteiro deve ser incrementado a cada vez.

 public class PassByReference { public static Integer inc(Integer i) { i = i+1; // I think that this must be **sneakally** creating a new integer... System.out.println("Inc: "+i); return i; } public static void main(String[] args) { Integer integer = new Integer(0); for (int i =0; i<10; i++){ inc(integer); System.out.println("main: "+integer); } } } 

Esta é minha saída esperada:

 Inc: 1
 principal: 1
 Inc: 2
 main: 2
 Inc: 3
 main: 3
 Inc: 4
 principal: 4
 Inc: 5
 main: 5
 Inc: 6
 principal: 6
 ...

Essa é a saída real.

 Inc: 1
 main: 0
 Inc: 1
 main: 0
 Inc: 1
 main: 0
 ...

Por que está se comportando assim?

Existem dois problemas:

  1. Inteiro é passado por valor, não por referência. Alterar a referência dentro de um método não será refletido na referência passada no método de chamada.
  2. O inteiro é imutável. Não existe esse método como Integer#set(i) . Você poderia simplesmente fazer uso dele.

Para fazê-lo funcionar, você precisa reatribuir o valor de retorno do método inc() .

 integer = inc(integer); 

Para aprender um pouco mais sobre a passagem pelo valor, aqui está outro exemplo:

 public static void main(String... args) { String[] strings = new String[] { "foo", "bar" }; changeReference(strings); System.out.println(Arrays.toString(strings)); // still [foo, bar] changeValue(strings); System.out.println(Arrays.toString(strings)); // [foo, foo] } public static void changeReference(String[] strings) { strings = new String[] { "foo", "foo" }; } public static void changeValue(String[] strings) { strings[1] = "foo"; } 

O inteiro é imutável. Você pode envolver int em sua class de wrapper personalizada.

 class WrapInt{ int value; } WrapInt theInt = new WrapInt(); inc(theInt); System.out.println("main: "+theInt.value); 

Existem 2 maneiras de passar por referência

  1. Use org.apache.commons.lang.mutable.MutableInt da biblioteca Apache Commons.
  2. Crie uma class personalizada como mostrado abaixo

Aqui está um exemplo de código para fazer isso:

 public class Test { public static void main(String args[]) { Integer a = new Integer(1); Integer b = a; Test.modify(a); System.out.println(a); System.out.println(b); IntegerObj ao = new IntegerObj(1); IntegerObj bo = ao; Test.modify(ao); System.out.println(ao.value); System.out.println(bo.value); } static void modify(Integer x) { x=7; } static void modify(IntegerObj x) { x.value=7; } } class IntegerObj { int value; IntegerObj(int val) { this.value = val; } } 

Saída:

 1 1 7 7 

Boas respostas acima explicando a questão real do OP.

Se alguém precisar passar um número que precise ser atualizado globalmente, use o AtomicInteger( ) em vez de criar as várias classs de wrapper sugeridas ou confiar em bibliotecas de terceiros.

O AtomicInteger( ) é, naturalmente, usado principalmente para access seguro a thread, mas se o acerto de desempenho não é problema, por que não usar essa class AtomicInteger( . O bônus adicional é, obviamente, a segurança óbvia do segmento.

 import java.util.concurrent.atomic.AtomicInteger 

O que você está vendo aqui não é um opressor + sobrecarregado, mas um comportamento de autoboxing. A class Integer é imutável e seu código:

 Integer i = 0; i = i + 1; 

é visto pelo compilador (após o autoboxing) como:

 Integer i = Integer.valueOf(0); i = Integer.valueOf(i.intValue() + 1); 

Então, você está correto em sua conclusão de que a instância Integer é alterada, mas não sorrateiramente – é consistente com a definição da linguagem Java 🙂

Você está correto aqui:

 Integer i = 0; i = i + 1; // <- I think that this is somehow creating a new object! 

Primeiro: Integer é imutável.

Segundo: a class Integer não está sobrescrevendo o operador + , há autounboxing e autoboxing envolvidos nessa linha (em versões mais antigas do Java você obteria um erro na linha acima).
Quando você escreve i + 1 o compilador primeiro converte o Integer em um int (primitivo) para realizar a adição: autounboxing. Em seguida, fazendo i = o compilador converte de int para um (novo) Integer: autoboxing.
Então, + está sendo aplicado a int primitivos.

Eu acho que é a autoboxing que está te jogando fora.

Esta parte do seu código:

  public static Integer inc(Integer i) { i = i+1; // I think that this must be **sneakally** creating a new integer... System.out.println("Inc: "+i); return i; } 

Realmente se resume ao código que se parece com:

  public static Integer inc(Integer i) { i = new Integer(i) + new Integer(1); System.out.println("Inc: "+i); return i; } 

Que claro .. não irá alterar a referência passada.

Você poderia consertá-lo com algo parecido com isto

  public static void main(String[] args) { Integer integer = new Integer(0); for (int i =0; i<10; i++){ integer = inc(integer); System.out.println("main: "+integer); } } 

Se você mudar sua function inc () para esta

  public static Integer inc(Integer i) { Integer iParam = i; i = i+1; // I think that this must be **sneakally** creating a new integer... System.out.println(i == iParam); return i; } 

então você verá que sempre imprime “falso”. Isso significa que a adição cria uma nova instância de Integer e a armazena na variável local i (“local”, porque i é na verdade uma cópia da referência que foi passada), deixando a variável do método de chamada intocada.

Integer é uma class imutável, o que significa que você não pode alterar seu valor, mas deve obter uma nova instância. Nesse caso, você não precisa fazer isso manualmente assim:

 i = new Integer(i+1); //actually, you would use Integer.valueOf(i.intValue()+1); 

em vez disso, é feito por autoboxing.