So führen Sie das Äquivalent von Referenzübergabe für Grundelemente in Java durch

115

Dieser Java-Code:

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

    void play(int toyNumber){  
        System.out.println("Toy number in play " + toyNumber);   
        toyNumber++;  
        System.out.println("Toy number in play after increement " + toyNumber);   
    }   
}  

gibt dies aus:

 
Spielzeugnummer im Spiel 5  
Spielzeugnummer im Spiel nach Vereinbarung 6  
Spielzeugnummer in Haupt 5  

In C ++ kann ich die toyNumberVariable als Referenz übergeben, um Schatten zu vermeiden, dh eine Kopie derselben Variablen wie unten zu erstellen:

void main(){  
    int toyNumber = 5;  
    play(toyNumber);  
    cout << "Toy number in main " << toyNumber << endl;  
}

void play(int &toyNumber){  
    cout << "Toy number in play " << toyNumber << endl;   
    toyNumber++;  
    cout << "Toy number in play after increement " << toyNumber << endl;   
} 

und die C ++ - Ausgabe lautet wie folgt:

Spielzeugnummer im Spiel 5  
Spielzeugnummer im Spiel nach Vereinbarung 6  
Spielzeugnummer in Haupt 6  

Meine Frage ist: Was ist der äquivalente Code in Java, um die gleiche Ausgabe wie der C ++ - Code zu erhalten, da Java eher als Wert als als Referenz übergeben wird ?

Student
quelle
22
Dies scheint mir kein Duplikat zu sein - die vermeintlich doppelte Frage befasst sich mit der Funktionsweise von Java und der Bedeutung der Begriffe Bildung. In dieser Frage wird speziell gefragt, wie man etwas bekommt, das einem Pass-by-Reference-Verhalten ähnelt, etwas, das vielen C entspricht / C ++ / D / Ada-Programmierer fragen sich möglicherweise, ob sie praktische Arbeit erledigen können, ohne sich darum zu kümmern, warum Java nur ein Wert für die Weitergabe ist.
DarenW
1
@DarenW Ich stimme voll und ganz zu - habe für die Wiedereröffnung gestimmt. Oh, und du hast genug Ruf, um dasselbe zu tun :-)
Duncan Jones
Die Diskussion um Primitive ist eher irreführend, da die Frage auch für Referenzwerte gilt.
Shmosel
"In C ++ kann ich die Variable toyNumber als Referenz übergeben, um Schatten zu vermeiden" - dies ist kein Schatten, da die toyNumberin der mainMethode deklarierte Variable in der Methode nicht im Gültigkeitsbereich liegt play. Das Shadowing in C ++ und Java erfolgt nur, wenn Bereiche verschachtelt sind. Siehe en.wikipedia.org/wiki/Variable_shadowing .
Stephen C

Antworten:

171

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

Auswahl 1: Machen Sie toyNumber zu einer öffentlichen Mitgliedsvariablen in einer Klasse

class MyToy {
  public int toyNumber;
}

Übergeben Sie dann einen Verweis auf ein MyToy an Ihre Methode.

void play(MyToy toy){  
    System.out.println("Toy number in play " + toy.toyNumber);   
    toy.toyNumber++;  
    System.out.println("Toy number in play after increement " + toy.toyNumber);   
}

Auswahl 2: Geben Sie den Wert zurück, anstatt ihn als Referenz zu übergeben

int play(int toyNumber){  
    System.out.println("Toy number in play " + toyNumber);   
    toyNumber++;  
    System.out.println("Toy number in play after increement " + toyNumber);   
    return toyNumber
}

Diese Auswahl würde eine kleine Änderung an der Call-Site in main erfordern, damit sie lautet , toyNumber = temp.play(toyNumber);.

Wahl 3: Machen Sie es zu einer Klasse oder statischen Variablen

Wenn die beiden Funktionen Methoden für dieselbe Klasse oder Klasseninstanz sind, können Sie toyNumber in eine Klassenmitgliedsvariable konvertieren.

Auswahl 4: Erstellen Sie ein einzelnes Elementarray vom Typ int und übergeben Sie dieses

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

void play(int [] toyNumber){  
    System.out.println("Toy number in play " + toyNumber[0]);   
    toyNumber[0]++;  
    System.out.println("Toy number in play after increement " + toyNumber[0]);   
}
laslowh
quelle
2
Klare Erklärung und besonders gut geeignet, um mehrere Optionen für die Codierung des gewünschten Effekts bereitzustellen. Direkt hilfreich für etwas, an dem ich gerade arbeite! Es ist verrückt, dass diese Frage geschlossen wurde.
DarenW
1
Obwohl Ihre Wahl 1 das tut, was Sie zu vermitteln versuchen, musste ich tatsächlich zurückgehen und es noch einmal überprüfen. In der Frage wird gefragt, ob Sie als Referenz übergeben können. Sie sollten also wirklich die Drucke in demselben Bereich erstellen, in dem das an die Funktion übergebene Spielzeug existiert. So wie Sie es haben, werden die Drucke in demselben Bereich wie das Inkrement betrieben, das den Sinn von COURSE nicht wirklich beweist Eine lokale Kopie, die nach einem Inkrement gedruckt wird, hat einen anderen Wert. Dies bedeutet jedoch nicht, dass die übergebene Referenz dies tut. Auch hier funktioniert Ihr Beispiel, beweist aber nicht den Punkt.
Null298
Nein, ich denke es ist richtig so wie es ist. Jede Funktion "play ()" in meiner obigen Antwort ist als Ersatz für die ursprüngliche Funktion "play ()" gedacht, die auch den Wert vor und nach dem Inkrementieren druckte. Die ursprüngliche Frage enthält hauptsächlich das println (), das sich als Referenz herausstellt.
Laslowh
Auswahl 2 bedarf weiterer Erläuterungen: Die Anrufstelle in main muss geändert werden, toyNumber = temp.play(toyNumber);damit sie wie gewünscht funktioniert.
ToolmakerSteve
1
Das ist extrem hässlich und hat ein hackiges Gefühl ... warum ist etwas so Grundlegendes nicht Teil der Sprache?
Shinzou
30

Java wird nicht als Referenz aufgerufen, sondern nur als Wert

Aber alle Variablen vom Objekttyp sind tatsächlich Zeiger.

Wenn Sie also ein veränderbares Objekt verwenden, sehen Sie das gewünschte Verhalten

public class XYZ {

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

    private static void play(StringBuilder toyNumber) {
        System.out.println("Toy number in play " + toyNumber);
        toyNumber.append(" + 1");
        System.out.println("Toy number in play after increement " + toyNumber);
    }
}

Ausgabe dieses Codes:

run:
Toy number in play 5
Toy number in play after increement 5 + 1
Toy number in main 5 + 1
BUILD SUCCESSFUL (total time: 0 seconds)

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

    List<Integer> mutableList = new ArrayList<Integer>();

    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:

run:
[1, 2, 3, 4, 5]
[3, 4, 1, 5, 2]
[1, 2, 3, 4, 5]
BUILD SUCCESSFUL (total time: 0 seconds)
Kerem Baydoğan
quelle
2
Dies ist keine Antwort auf die Frage. Es hätte die Frage beantwortet, wenn es vorgeschlagen hätte, ein ganzzahliges Array zu erstellen, das ein einzelnes Element enthält, und dieses Element dann innerhalb der Methode zu playändern. zB mutableList[0] = mutableList[0] + 1;. Wie Ernest Friedman-Hill vorschlägt.
ToolmakerSteve
Mit Nichtprimitiven. Der Wert des Objekts ist keine Referenz. Vielleicht wollten Sie sagen: "Parameterwert" ist "eine Referenz". Referenzen werden als Wert übergeben.
Vlakov
Ich versuche etwas Ähnliches zu tun, aber in Ihrem Code die Methode private static void play(StringBuilder toyNumber)- wie nennt man das, wenn es zum Beispiel public static int ist, das eine Ganzzahl zurückgibt? Da ich eine Methode habe, die eine Nummer zurückgibt, diese aber nicht irgendwo anruft, wird sie nicht verwendet.
Frank17
18

Mach ein

class PassMeByRef { public int theValue; }

Übergeben Sie dann einen Verweis auf eine Instanz davon. Beachten Sie, dass eine Methode, die den Status durch ihre Argumente mutiert, am besten vermieden wird, insbesondere im parallelen Code.

Ingo
quelle
3
Von mir herabgestimmt. Das ist auf so vielen Ebenen falsch. Java wird immer als Wert übergeben. Keine Ausnahmen.
Duffymo
14
@duffymo - Natürlich können Sie nach Belieben abstimmen - aber haben Sie darüber nachgedacht, was das OP gefragt hat? Er möchte als Referenz übergeben und int - und genau dies wird erreicht, wenn ich einen Verweis auf eine Instanz des oben genannten als Wert übergebe.
Ingo
7
@ Duffymo: Huh? Sie irren sich oder verstehen die Frage falsch. Das IST eine der traditionellen Art und Weise in Java zu tun , was die OP - Anforderungen.
ToolmakerSteve
5
@duffymo: Dein Kommentar ist auf so vielen Ebenen falsch. Bei SO geht es darum, nützliche Antworten und Kommentare bereitzustellen, und Sie stimmen einfach eine richtige Antwort ab, nur weil sie (glaube ich) nicht zu Ihrem Programmierstil und Ihrer Philosophie passt. Könnten Sie zumindest eine bessere Erklärung oder sogar eine bessere Antwort auf die ursprüngliche Frage geben?
Paercebal
2
@duffymo das ist genau das, was ich gesagt habe: übergebe einen ref by value. Wie wird Pass by Ref Ihrer Meinung nach in Sprachen erreicht, die es verlangsamen?
Ingo
11

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

In einer Situation, in der Sie wirklich ein Grundelement als Referenz übergeben müssen, deklarieren die Benutzer manchmal den Parameter als Array vom primitiven Typ und übergeben dann ein Array mit einem einzelnen Element als Argument. Sie übergeben also eine Referenz int [1] und können in der Methode den Inhalt des Arrays ändern.

Ernest Friedman-Hill
quelle
1
Nein - alle Wrapper-Klassen sind unveränderlich - sie stellen einen festen Wert dar, der nach dem Erstellen des Objekts nicht mehr geändert werden kann.
Ernest Friedman-Hill
1
"Sie können Primitive in Java nicht als Referenz übergeben": Das OP scheint dies zu verstehen, da sie C ++ (wo Sie können) mit Java kontrastieren.
Raedwald
Es sollte beachtet werden, dass "alle Wrapper-Klassen unveränderlich sind", wie von Ernest angegeben, für JDKs integrierte Integer-, Double-, Long- usw. gilt. Sie können diese Einschränkung mit Ihrer benutzerdefinierten Wrapper-Klasse umgehen, wie es Ingo's Answer getan hat.
user3207158
10

Für eine schnelle Lösung können Sie AtomicInteger oder eine der atomaren Variablen verwenden, mit denen Sie den Wert innerhalb der Methode mithilfe der integrierten Methoden ändern können. Hier ist Beispielcode:

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 before method Call:" + myNumber.get());
        PrimitivePassByReferenceSample temp = new PrimitivePassByReferenceSample() ;
        temp.changeMyNumber(myNumber);
        System.out.println("MyNumber After method Call:" + myNumber.get());


    }

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

    }

}

Ausgabe:

MyNumber before method Call:0

MyNumber After method Call:100
premSiva
quelle
Ich verwende und mag diese Lösung, weil sie nette zusammengesetzte Aktionen bietet und einfacher in gleichzeitige Programme zu integrieren ist, die diese Atomklassen bereits oder möglicherweise in Zukunft verwenden möchten.
RAnders00
2
public static void main(String[] args) {
    int[] toyNumber = new int[] {5};
    NewClass temp = new NewClass();
    temp.play(toyNumber);
    System.out.println("Toy number in main " + toyNumber[0]);
}

void play(int[] toyNumber){
    System.out.println("Toy number in play " + toyNumber[0]);
    toyNumber[0]++;
    System.out.println("Toy number in play after increement " + toyNumber[0]);
}
itun
quelle