¿Cómo puedo pasar una clase entera correctamente por referencia?

Espero que alguien pueda aclarar lo que está sucediendo aquí para mí. Busqué en la clase entera un poco, pero debido a que el entero está anulando el operador + no pude entender qué estaba pasando mal. Mi problema es con esta línea:

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

Aquí está mi razonamiento: sé que java es pasar por valor ( o pasar por el valor de referencia ), así que creo que en el siguiente ejemplo, el objeto entero debe incrementarse 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); } } } 

Este es mi resultado esperado:

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

Esta es la salida real.

 Inc: 1
 principal: 0
 Inc: 1
 principal: 0
 Inc: 1
 principal: 0
 ...

¿Por qué se está comportando así?

Hay dos problemas:

  1. El entero se pasa por valor, no por referencia. Cambiar la referencia dentro de un método no se reflejará en la referencia pasada en el método de llamada.
  2. El entero es inmutable. No existe un método como Integer#set(i) . De lo contrario, podría simplemente hacer uso de él.

Para que funcione, debe reasignar el valor de retorno del método inc() .

 integer = inc(integer); 

Para aprender un poco más sobre pasar por valor, aquí hay otro ejemplo:

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

El entero es inmutable. Puede envolver int en su clase contenedora personalizada.

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

Hay 2 formas de pasar por referencia

  1. Use org.apache.commons.lang.mutable.MutableInt de la biblioteca de Apache Commons.
  2. Crear clase personalizada como se muestra a continuación

Aquí hay un código de muestra para hacerlo:

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

Salida:

 1 1 7 7 

Buenas respuestas arriba explicando la pregunta real del OP.

Si alguien necesita pasar un número que necesita ser actualizado globalmente, use AtomicInteger( ) en lugar de crear las diversas clases de contenedor sugeridas o confiar en libs de terceros.

El AtomicInteger( ) se utiliza principalmente para el acceso seguro de subprocesos, pero si el rendimiento alcanzado no es un problema, ¿por qué no utilizar esta clase incorporada. La ventaja añadida es, por supuesto, la seguridad del hilo obvio.

 import java.util.concurrent.atomic.AtomicInteger 

Lo que está viendo aquí no es un opacador + sobrecargado, sino un comportamiento de autoboxing. La clase Integer es inmutable y tu código:

 Integer i = 0; i = i + 1; 

es visto por el comstackdor (después del autoboxing) como:

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

por lo tanto, tiene razón en su conclusión de que la instancia de Integer está modificada, pero no furtivamente, es coherente con la definición de lenguaje Java 🙂

Usted está en lo correcto aquí:

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

Primero: El entero es inmutable.

Segundo: la clase Integer no anula el operador + , hay autoenvasado y autoboxing involucrados en esa línea (en las versiones anteriores de Java, se obtendría un error en la línea anterior).
Cuando escribe i + 1 el comstackdor primero convierte el entero en un int (primitivo) para realizar la adición: autounboxing. Luego, al hacer i = el comstackdor convierte de int a un (nuevo) Entero: autoboxing.
Entonces + se está aplicando realmente a primitive int s.

Creo que es el autoboxing el que te está tirando.

Esta parte de tu 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 reduce a un código que se ve así:

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

Lo cual por supuesto … no cambiará la referencia pasada.

Podrías arreglarlo con algo como esto

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

Si cambia su función inc () a este

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

entonces verá que siempre imprime “falso”. Eso significa que la adición crea una nueva instancia de Integer y la almacena en la variable local i (“local”, porque en realidad es una copia de la referencia que se pasó), dejando intacta la variable del método de llamada.

El entero es una clase inmutable, lo que significa que no puede cambiar su valor sino que debe obtener una nueva instancia. En este caso, no tiene que hacerlo manualmente de esta manera:

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

en cambio, se hace por autoboxing.