126 Stimmen

Wie man das Äquivalent von Verweisübergabe für Primitivdatentypen in Java macht

Dieser Java-Code:

public class XYZ {   
    public static void main(){  
        int toyNumber = 5;   
        XYZ temp = new XYZ();  
        temp.spielen(toyNumber);  
        System.out.println("Spielzeugnummer in main " + toyNumber);  
    }

    void spielen(int toyNumber){  
        System.out.println("Spielzeugnummer im Spiel " + toyNumber);   
        toyNumber++;  
        System.out.println("Spielzeugnummer im Spiel nach Inkrementierung " + toyNumber);   
    }   
}  

wird folgendes ausgeben:

Spielzeugnummer im Spiel 5  
Spielzeugnummer im Spiel nach Inkrementierung 6  
Spielzeugnummer in main 5  

In C++ kann ich die toyNumber-Variable als Referenzübergabe übergeben, um Schattenbildung zu vermeiden, d.h. eine Kopie derselben Variable zu erstellen, wie unten gezeigt:

void main(){  
    int toyNumber = 5;  
    spielen(toyNumber);  
    cout << "Spielzeugnummer in main " << toyNumber << endl;  
}

void spielen(int &toyNumber){  
    cout << "Spielzeugnummer im Spiel " << toyNumber << endl;   
    toyNumber++;  
    cout << "Spielzeugnummer im Spiel nach Inkrementierung " << toyNumber << endl;   
} 

und die Ausgabe in C++ wird sein:

Spielzeugnummer im Spiel 5  
Spielzeugnummer im Spiel nach Inkrementierung 6  
Spielzeugnummer in main 6  

Meine Frage ist - Was ist der äquivalente Code in Java, um die gleiche Ausgabe wie der C++-Code zu erhalten, unter der Voraussetzung, dass Java pass-by-value anstelle von pass-by-reference ist?

26 Stimmen

Das scheint für mich nicht wie ein Duplikat - die angeblich duplizierte Frage behandelt, wie Java funktioniert und was die Begriffe bedeuten, was Bildung ist, während diese Frage speziell danach fragt, wie man ein Verhalten erhält, das dem Referenzwertähnelt, etwas, was viele C/C++/D/Ada-Programmierer sich fragen könnten, um praktische Arbeit zu erledigen, ohne sich darum zu kümmern, warum Java alles nach Wert übermittelt.

1 Stimmen

@DarenW Ich stimme vollkommen zu - habe für die Wiedereröffnung gestimmt. Oh, und du hast genug Reputation, um dasselbe zu tun :-)

0 Stimmen

Die Diskussion um Primitivtypen ist eher irreführend, da sich die Frage gleichermaßen auf Referenzwerte bezieht.

187voto

laslowh Punkte 8254

Sie haben mehrere Möglichkeiten. Die eine, die am sinnvollsten ist, hängt wirklich davon ab, was Sie versuchen zu tun.

Wahl 1: Machen Sie toyNumber zu einer öffentlichen Member-Variablen in einer Klasse

class MyToy {
  public int toyNumber;
}

dann übergeben Sie eine Referenz auf ein MyToy-Objekt an Ihre Methode.

void play(MyToy toy){  
    System.out.println("Spielzeugnummer im Spiel " + toy.toyNumber);   
    toy.toyNumber++;  
    System.out.println("Spielzeugnummer im Spiel nach der Erhöhung " + toy.toyNumber);   
}

Wahl 2: Gib den Wert zurück anstatt als Referenz zu übergeben

int play(int toyNumber){  
    System.out.println("Spielzeugnummer im Spiel " + toyNumber);   
    toyNumber++;  
    System.out.println("Spielzeugnummer im Spiel nach der Erhöhung " + toyNumber);   
    return toyNumber
}

Diese Wahl würde eine kleine Änderung am Aufruf der Methode in der Main erfordern, damit es heißt toyNumber = temp.play(toyNumber);.

Wahl 3: Machen Sie es zu einer Klassen- oder statischen Variable

Wenn die beiden Funktionen Methoden der gleichen Klasse oder Klasseinstanz sind, könnten Sie toyNumber in eine Klassenmember-Variable umwandeln.

Wahl 4: Erstellen Sie ein Array mit einem einzelnen Element vom Typ int und übergeben Sie das

Dies wird als Hack betrachtet, wird aber manchmal verwendet, um Werte aus Inline-Klassenaufrufen zurückzugeben.

void play(int [] toyNumber){  
    System.out.println("Spielzeugnummer im Spiel " + toyNumber[0]);   
    toyNumber[0]++;  
    System.out.println("Spielzeugnummer im Spiel nach der Erhöhung " + toyNumber[0]);   
}

3 Stimmen

Klare Erklärung und besonders gut geeignet für die Bereitstellung mehrerer Auswahlmöglichkeiten, wie man den gewünschten Effekt codieren kann. Direkt hilfreich für das, woran ich gerade arbeite! Es ist verrückt, dass diese Frage geschlossen wurde.

1 Stimmen

Obwohl Ihre Wahl 1 tatsächlich das tut, was Sie ausdrücken möchten, musste ich tatsächlich zurückgehen und es überprüfen. Die Frage lautet, ob Sie per Verweis übergeben können; daher sollten Sie die printlns wirklich im selben Bereich gemacht haben, in dem das Spielzeug besteht, das an die Funktion übergeben wird. Auf die von Ihnen gewählte Weise werden die printlns im selben Bereich wie die Inkrementierung ausgeführt, was den Punkt nicht wirklich beweist. NATÜRLICH wird eine lokale Kopie, die nach einem Inkrement gedruckt wird, einen anderen Wert haben, aber das bedeutet nicht, dass der übergebene Verweis dies auch hat. Trotzdem funktioniert Ihr Beispiel, beweist aber nicht den Punkt.

0 Stimmen

Nein, ich denke, es ist korrekt, wie es ist. Jede Funktion "play()" in meiner obigen Antwort ist als Ersatz für die ursprüngliche "play()" Funktion gedacht, die auch den Wert vor und nach der Inkrementierung gedruckt hat. Die ursprüngliche Frage hat das println() in main, das den Übergabe nach Verweis beweist.

30voto

Kerem Baydoğan Punkte 10172

Java ist nicht call by reference. es ist nur call by value

Aber alle Variablen vom Objekttyp sind tatsächlich Zeiger.

Also, wenn Sie ein veränderliches Objekt verwenden, werden Sie das gewünschte Verhalten sehen

public class XYZ {

    public static void main(String[] arg) {
        StringBuilder toyNumber = new StringBuilder("5");
        play(toyNumber);
        System.out.println("Spielzeugnummer in main " + toyNumber);
    }

    private static void play(StringBuilder toyNumber) {
        System.out.println("Spielzeugnummer in play " + toyNumber);
        toyNumber.append(" + 1");
        System.out.println("Spielzeugnummer in play nach der Erhöhung " + toyNumber);
    }
}

Ausgabe dieses Codes:

ausführen:
Spielzeugnummer in play 5
Spielzeugnummer in play nach der Erhöhung 5 + 1
Spielzeugnummer in main 5 + 1
ERFOLGREICH ERSTELLT (Gesamtzeit: 0 Sekunden)

Sie können dieses Verhalten auch in Standardbibliotheken sehen. Zum Beispiel Collections.sort(); Collections.shuffle(); Diese Methoden geben keine neue Liste zurück, sondern ändern das übergebene Objekt.

    List mutableList = new ArrayList();

    mutableList.add(1);
    mutableList.add(2);
    mutableList.add(3);
    mutableList.add(4);
    mutableList.add(5);

    System.out.println(mutableList);

    Collections.shuffle(mutableList);

    System.out.println(mutableList);

    Collections.sort(mutableList);

    System.out.println(mutableList);

Ausgabe dieses Codes:

ausführen:
[1, 2, 3, 4, 5]
[3, 4, 1, 5, 2]
[1, 2, 3, 4, 5]
ERFOLGREICH ERSTELLT (Gesamtzeit: 0 Sekunden)

3 Stimmen

Dies ist keine Antwort auf die Frage. Es hätte die Frage beantwortet, wenn es vorgeschlagen hätte, ein Integer-Array zu erstellen, das ein einziges Element enthält, und dieses Element innerhalb der Methode play zu modifizieren; z. B. mutableList[0] = mutableList[0] + 1; . Wie Ernest Friedman-Hill vorschlägt.

0 Stimmen

Mit Nicht-Primitiven. Der Wert des Objekts ist keine Referenz. Vielleicht wollten Sie sagen: "Parameterwert" ist "eine Referenz". Referenzen werden nach Wert übergeben.

0 Stimmen

Ich versuche etwas Ähnliches zu tun, aber in deinem Code ist die Methode private static void play(StringBuilder toyNumber) - wie rufe ich sie auf, wenn sie zum Beispiel public static int ist und einen Ganzzahlwert zurückgibt? Denn ich habe eine Methode, die eine Zahl zurückgibt, aber wenn ich sie nicht irgendwo aufrufe, wird sie nicht verwendet.

19voto

Ingo Punkte 35534

Machen Sie eine

Klasse PassMeByRef { public int derWert; }

dann übergeben Sie eine Referenz auf eine Instanz davon. Beachten Sie, dass eine Methode, die den Zustand durch ihre Argumente verändert, am besten vermieden wird, insbesondere in parallelem Code.

3 Stimmen

Von mir abgestimmt. Das ist auf so vielen Ebenen falsch. Java ist immer wertorientiert - immer. Keine Ausnahmen.

17 Stimmen

@duffymo - Natürlich kannst du nach Belieben abstimmen - aber hast du darüber nachgedacht, was der OP gefragt hat? Er möchte eine Integer per Referenz übergeben - und das wird gerade erreicht, wenn ich per Wert eine Referenz auf eine Instanz des obigen übergebe.

8 Stimmen

@duffymo: Hä? Du irrst dich, oder missverstehst die Frage. Dies IST eine der traditionellen Methoden in Java, um das zu tun, was der OP verlangt.

11voto

Ernest Friedman-Hill Punkte 79357

Sie können in Java keine Primitiven per Referenz übergeben. Alle Variablen vom Objekttyp sind tatsächlich Zeiger, aber wir nennen sie "Referenzen" und sie werden auch immer nach Wert übergeben.

In einer Situation, in der Sie wirklich einen primitiven Wert per Referenz übergeben müssen, ist das, was die Leute manchmal tun, den Parameter als Array vom primitiven Typ zu deklarieren und dann ein Array mit einem Element als Argument zu übergeben. So übergeben Sie eine Referenz int[1] und im Methoden können Sie den Inhalt des Arrays ändern.

1 Stimmen

Nein - alle Wrapper-Klassen sind unveränderlich - sie repräsentieren einen festen Wert, der nicht geändert werden kann, sobald das Objekt erstellt wurde.

1 Stimmen

"Du kannst in Java keine primitiven Datentypen per Referenz übergeben": Der OP scheint dies zu verstehen, da er C++ (wo dies möglich ist) mit Java vergleicht.

1 Stimmen

Es sei darauf hingewiesen, dass "alle Wrapper-Klassen unveränderlich sind", wie von Ernest gesagt, auf die integrierten Integer-, Double-, Long-Klassen usw. von JDK zutrifft. Sie können diese Einschränkung mit Ihrer individuellen Wrapper-Klasse umgehen, ähnlich wie es die Antwort von Ingo getan hat.

11voto

premSiva Punkte 111

Für eine schnelle Lösung können Sie AtomicInteger oder eine der atomaren Variablen verwenden, die es Ihnen ermöglichen, den Wert innerhalb der Methode mit den integrierten Methoden zu ändern. Hier ist Beispielscode:

import java.util.concurrent.atomic.AtomicInteger;

public class PrimitivePassByReferenceSample {

    /**
     * @param args
     */
    public static void main(String[] args) {

        AtomicInteger myNumber = new AtomicInteger(0);
        System.out.println("MyNumber vor dem Methodenaufruf:" + myNumber.get());
        PrimitivePassByReferenceSample temp = new PrimitivePassByReferenceSample() ;
        temp.changeMyNumber(myNumber);
        System.out.println("MyNumber nach dem Methodenaufruf:" + myNumber.get());

    }

     void changeMyNumber(AtomicInteger myNumber) {
        myNumber.getAndSet(100);

    }

}

Ausgabe:

MyNumber vor dem Methodenaufruf:0

MyNumber nach dem Methodenaufruf:100

1 Stimmen

Ich verwende und mag diese Lösung, weil sie schöne zusammengesetzte Aktionen bietet und sich einfacher in gleichzeitige Programme integrieren lässt, die bereits oder möglicherweise in Zukunft diese atomaren Klassen verwenden möchten.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X