Подтвердить что ты не робот

Как передать класс Integer по ссылке?

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

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
...

Почему это так?

4b9b3361

Ответ 1

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

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

Ответ 2

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

class WrapInt{
    int value;
}

WrapInt theInt = new WrapInt();

inc(theInt);
System.out.println("main: "+theInt.value);

Ответ 3

Есть 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

Ответ 4

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

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

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

import java.util.concurrent.atomic.AtomicInteger

Ответ 5

То, что вы видите здесь, не перегруженное oparator +, а поведение autoboxing. Класс Integer неизменен и ваш код:

Integer i = 0;
i = i + 1;  

рассматривается компилятором (после автобокса) следующим образом:

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

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

Ответ 6

Вы здесь верны:

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

Во-первых: Integer неизменен.

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

Ответ 7

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

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

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

Ответ 8

Если вы измените свою функцию 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;
  }

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

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

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

вместо этого выполняется автобоксинг.

Ответ 9

1) Только копия ссылки отправляется в качестве значения формального параметра. Когда переменной формального параметра назначается другое значение, ссылка формального параметра изменяется, но фактическая ссылка параметра остается той же самой в случае этого целочисленного объекта.

открытый класс UnderstandingObjects {

public static void main(String[] args) {

    Integer actualParam = new Integer(10);

    changeValue(actualParam);

    System.out.println("Output " + actualParam); // o/p =10

    IntObj obj = new IntObj();

    obj.setVal(20);

    changeValue(obj);

    System.out.println(obj.a); // o/p =200

}

private static void changeValue(Integer formalParam) {

    formalParam = 100;

    // Only the copy of reference is set to the formal parameter
    // this is something like => Integer formalParam =new Integer(100);
    // Here we are changing the reference of formalParam itself not just the
    // reference value

}

private static void changeValue(IntObj obj) {
    obj.setVal(200);

    /*
     * obj = new IntObj(); obj.setVal(200);
     */
    // Here we are not changing the reference of obj. we are just changing the
    // reference obj value

    // we are not doing obj = new IntObj() ; obj.setValue(200); which has happend
    // with the Integer

}

}

class IntObj {   Целое число;

public void setVal(int a) {
    this.a = a;
}

}