Как правильно передать класс Integer по ссылке? - PullRequest
54 голосов
/ 26 июля 2010

Я надеюсь, что кто-то сможет уточнить, что здесь происходит для меня. Я немного покопался в целочисленном классе, но поскольку целое число переопределяет оператор +, я не мог понять, что происходит не так. Моя проблема с этой строкой:

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

Вот мои рассуждения: Я знаю, что java передается по значению ( или передается по значению ссылки ), поэтому я думаю, что в следующем примере целочисленный объект должен увеличиваться каждый раз.

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

Это мой ожидаемый результат:

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

Это фактический результат.

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

Почему он так себя ведет?

Ответы [ 8 ]

55 голосов
/ 26 июля 2010

Есть две проблемы:

  1. Целое число передается по значению, а не по ссылке.Изменение ссылки внутри метода не будет отражено в переданной ссылке в вызывающем методе.
  2. Целое число является неизменным.Нет такого метода, как Integer#set(i).В противном случае вы можете просто использовать его.

Чтобы заставить его работать, вам необходимо переназначить возвращаемое значение метода inc().

integer = inc(integer);

Чтобы узнать немного больше о передаче по значению, вот еще один пример:

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";
}
18 голосов
/ 26 июля 2010

Целое число является неизменным.Вы можете обернуть int в свой класс обёртки.

class WrapInt{
    int value;
}

WrapInt theInt = new WrapInt();

inc(theInt);
System.out.println("main: "+theInt.value);
15 голосов
/ 11 октября 2014

Существует 2 способа передачи по ссылке

  1. Использование org.apache.commons.lang.mutable.MutableInt из библиотеки Apache Commons.
  2. Создайте пользовательский класс, как показано ниже

Вот пример кода для этого:

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

Вывод:

1
1
7
7
14 голосов
/ 26 февраля 2015

Хорошие ответы выше, объясняющие фактический вопрос из ОП.

Если кому-то нужно передать число, которое необходимо глобально обновить, используйте AtomicInteger() вместо создания различных классов-оболочек, предлагаемых или использующих сторонние библиотеки.

AtomicInteger(), конечно, в основном используется для безопасного доступа к потокам, но если снижение производительности не является проблемой, почему бы не использовать этот встроенный класс.Дополнительный бонус - это, конечно, очевидная безопасность потока.

import java.util.concurrent.atomic.AtomicInteger
12 голосов
/ 26 июля 2010

Здесь вы видите не перегруженный + опаратор, а поведение при автобоксировании.Класс Integer является неизменным, и ваш код:

Integer i = 0;
i = i + 1;  

видится компилятором (после автобоксирования) как:

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

, поэтому вы правы в том, чтоInteger экземпляр изменен, но не скрытно - это соответствует определению языка Java: -)

6 голосов
/ 26 июля 2010

Вы правы здесь:

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

Первое: целое число является неизменным.

Во-вторых: класс Integer не переопределяет оператор +, в этой строке присутствуют автобокс и автобокс (в более старых версиях Java вы получите сообщение об ошибке в приведенной выше строке).
Когдавы пишете i + 1, компилятор сначала преобразует Integer в (примитив) int для выполнения сложения: autounboxing.Затем, выполнив i = <some int>, компилятор преобразует из int в (новое) целое число: автобокс.
Таким образом, + фактически применяется к примитиву int s.

2 голосов
/ 26 июля 2010

Если вы измените свою функцию inc () на эту

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

тогда вы увидите, что он всегда печатает «ложь». Это означает, что дополнение создает новый экземпляр Integer и сохраняет его в переменной local i («local», потому что на самом деле i является копией переданной ссылки), оставляя переменную вызывающего метод не тронут.

Integer - неизменный класс, означающий, что вы не можете изменить его значение, но должны получить новый экземпляр. В этом случае вам не нужно делать это вручную следующим образом:

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

Вместо этого, это делается с помощью автобокса.

2 голосов
/ 26 июля 2010

Я думаю, что это автобокс, который сбивает вас с толку.

Эта часть вашего кода:

   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 Integer inc(Integer i) {
        i = new Integer(i) + new Integer(1);      
        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++){
            integer = inc(integer);
            System.out.println("main: "+integer);
        }
    }
...