Jak mogę poprawnie przekazać klasę Integer przez odniesienie?

Mam nadzieję, że ktoś wyjaśni mi, co się tu dzieje. Poszperałem trochę w klasie integer, ale ponieważ integer jest nadrzędnym operatorem +, nie mogłem zrozumieć, co poszło nie tak. Mój problem jest z tą linijką:

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

Oto moje rozumowanie: Wiem, że java jest pass by value (lub pass by value of reference ), więc myślę, że w poniższym przykładzie obiekt integer powinien być za każdym razem zwiększany.

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

To jest moim oczekiwanym efektem:

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

To jest rzeczywiste wyjście.

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

Dlaczego się tak zachowuje?

Author: Community, 2010-07-26

8 answers

Są dwa problemy:

  1. liczba całkowita jest przekazywana przez wartość, nie przez odniesienie. Zmiana referencji wewnątrz metody nie zostanie odzwierciedlona w referencji przekazanej w wywołującej metodzie.
  2. liczba całkowita jest niezmienna. Nie ma takiej metody jak Integer#set(i). W przeciwnym razie możesz z tego skorzystać.

Aby go uruchomić, musisz ponownie przypisać wartość zwracaną metody inc().

integer = inc(integer);

Aby dowiedzieć się więcej o przekazywaniu wartości, oto kolejny przykład:

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";
}
 48
Author: BalusC,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-01 10:11:04

Liczba całkowita jest niezmienna. Możesz owinąć int w niestandardową klasę owijania.

class WrapInt{
    int value;
}

WrapInt theInt = new WrapInt();

inc(theInt);
System.out.println("main: "+theInt.value);
 17
Author: Markos,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-07-25 20:35:55

Istnieją 2 sposoby, aby przejść przez odniesienie

  1. Użyj org.Apacz.commons.lang.mutable.MutableInt z biblioteki Apache Commons.
  2. Utwórz własną klasę, jak pokazano poniżej

Oto przykładowy kod, aby to zrobić:

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

Wyjście:

1
1
7
7
 14
Author: Rishi Dua,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-28 06:39:23

Dobre odpowiedzi powyżej wyjaśniające rzeczywiste pytanie z OP.

Jeśli ktoś musi przekazać liczbę, która musi być globalnie zaktualizowana, użyj AtomicInteger() zamiast tworzyć różne sugerowane klasy wrapperów lub polegać na bibliotekach stron trzecich.

The AtomicInteger() jest oczywiście używany głównie do bezpiecznego dostępu do wątku, ale jeśli wydajność nie jest problemem, dlaczego nie użyć tej wbudowanej klasy. Dodatkowym bonusem jest oczywiście oczywiste bezpieczeństwo wątku.

import java.util.concurrent.atomic.AtomicInteger
 11
Author: Hans,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-02-26 12:39:58

To, co tu widzisz, nie jest przeciążonym oparatorem, ale zachowaniem autoboxingu. Klasa Integer jest niezmienna, a Twój kod:

Integer i = 0;
i = i + 1;  

Jest postrzegane przez kompilator (po autoboxingu) jako:

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

Więc masz rację w swoim wniosku, że instancja Integer została zmieniona, ale nie podstępnie - jest zgodna z definicją języka Java: -)

 9
Author: rsp,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-07-25 20:35:17

Masz rację tutaj:

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

Pierwszy: liczba całkowita jest niezmienna.

Po Drugie: Klasa Integer nie nadpisuje operatora +, w tej linii występuje autounboxing i autoboxing (w starszych wersjach Javy pojawiałby się błąd w powyższej linii).
Kiedy piszesz i + 1 kompilator najpierw Konwertuje liczbę całkowitą na (prymitywną) int w celu wykonania dodawania: autounboxing. Następnie wykonujemy i = <some int> kompilator konwertuje z int na (nową) liczbę całkowitą: autoboxing.
Tak więc + jest faktycznie stosowane do prymitywnych ints.

 5
Author: Carlos Heuberger,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-01 08:14:29

Myślę, że to autoboxing Cię odrzuca.

Ta część Twojego kodu:

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

Naprawdę sprowadza się do kodu, który wygląda następująco:

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

Które oczywiście.. nie zmieni przekazanego odniesienia.

Możesz to naprawić czymś takim

  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);
        }
    }
 2
Author: bwawok,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-07-25 20:31:12

Jeśli zmienisz funkcję inc () na tę

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

Wtedy zobaczysz, że zawsze wyświetla "false". Oznacza to, że dodatek tworzy nową instancję Integer i przechowuje ją w zmiennej local i ("local", ponieważ i jest w rzeczywistości kopią referencji, która została przekazana), pozostawiając zmienną wywołującej metodę nietkniętą.

Integer jest klasą niezmienną, co oznacza, że nie możesz zmienić jej wartości, ale musisz uzyskać nową instancję. W tym przypadku nie masz zrobić to ręcznie w ten sposób:

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

Zamiast tego odbywa się to za pomocą autoboxingu.

 2
Author: f1sh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-07-25 20:35:41