Hat Java so etwas wie die Ref- und Out-Schlüsselwörter von C #?

113

So etwas wie das Folgende:

Referenzbeispiel:

void changeString(ref String str) {
    str = "def";
}

void main() {
    String abc = "abc";
    changeString(ref abc);
    System.out.println(abc); //prints "def"
}

Beispiel:

void changeString(out String str) {
    str = "def";
}

void main() {
    String abc;
    changeString(out abc);
    System.out.println(abc); //prints "def"
}
verschlungenes Elysium
quelle
1
Mögliches Duplikat von Kann ich Parameter als Referenz in Java übergeben?
Robert Harvey
5
IMO fehlt dir nicht viel. Das einzige Mal, dass ich refoder outin C # verwende, ist, wenn ich ein Muster wie verwende TryParse(), bei dem die Methode ein boolesches Ergebnis zurückgibt, und die einzige Möglichkeit, einen analysierten Wert daraus zu erhalten, ist die Verwendung von refoder out.
Robert Harvey
3
Ratet mal, das ist richtig, was ich verwenden muss! ;)
verschlungenes Elysium
Die andere Möglichkeit besteht darin, ein zusammengesetztes Objekt mit dem Status und einem nullbaren Wert zurückzugeben. Aber ich gebe zu, das ist ein bisschen Rube Goldberg-artig.
Robert Harvey
1
Es ist nichts Falsches daran, ein zusammengesetztes Objekt zurückzugeben, wenn es nur ein vordefiniertes verwendbares Objekt (dh Tupel) geben würde. Aber warte, das würde nicht gelöschte Generika erfordern, die mit primitiven Typen arbeiten, um effizient zu sein :)
Pavel Minaev

Antworten:

102

Nein, Java verfügt nicht über C # refund outSchlüsselwörter, die als Referenz übergeben werden können.

Sie können in Java nur Werte übergeben. Auch Referenzen werden als Wert übergeben. Siehe Jon Skeet ‚s Seite über Parameter in Java vorbei für weitere Details.

Um etwas zu tun ähnlich wie refoder outSie müßten Ihre Parameter in einem anderen Objekt wickeln und das Objekt Verweis übergeben als Parameter.

Mark Byers
quelle
5
Dies sollte auf einige erweitert werden. Sie können nur Grundelemente (int, short, char usw.) als Wert übergeben. Und nein, es gibt kein Out.
Corey Sunwold
14
Das ist nicht 100% wahr. Wenn Sie ein Array oder eine Klasse übergeben, wird der Verweis auf das Array oder Objekt als Wert übergeben. Sie können Interna in das Array oder Objekt ändern und es wird im Aufrufer wiedergegeben.
Romain Hippeau
12
@fearofawhackplanet: Ähm, es sei denn, Sie verwenden ref.
Robert Harvey
2
@ Angstofawhackplanet: Reference parameters need the ref modifier as part of both the declaration and the invocation - that means it's always clear when you're passing something by reference. yoda.arachsys.com/csharp/parameters.html
Mark Byers
5
Aus CLR-Sicht übergeben Sie eine verwaltete Referenz ( T&) als Wert, ja. Aber C # hat eine eigene Terminologie und enthält insbesondere keine Werte vom Typ ref T- aus C # -Perspektive refist es ausschließlich ein Parametermodifikator, und es macht keinen Sinn, davon zu sprechen, dass eine Referenz als Wert übergeben wird .
Pavel
30

Direkte Antwort: Nein

Sie können die Referenz jedoch mit Wrappern simulieren .

Und machen Sie folgendes:

void changeString( _<String> str ) {
    str.s("def");
}

void testRef() {
     _<String> abc = new _<String>("abc");
     changeString( abc );
     out.println( abc ); // prints def
}

aus

void setString( _<String> ref ) {
    str.s( "def" );
}
void testOut(){
    _<String> abc = _<String>();
    setString( abc );
    out.println(abc); // prints def
}

Und im Grunde jeder andere Typ wie:

_<Integer> one = new <Integer>(1);
addOneTo( one );

out.println( one ); // May print 2
OscarRyz
quelle
28
Autsch. Das ist hässlich.
verschlungenes Elysium
46
Ich habe nie gesagt, Sie können es auf diese elegante Weise tun : P
OscarRyz
Warum funktioniert Folgendes nicht: private static void ParseLine (String newline, String [] aWrapper, Integer [] bWrapper) {StringTokenizer st = new StringTokenizer (newline); aWrapper [0] = st.nextToken (); bWrapper [0] = neue Ganzzahl (st.nextToken ()); } ParseLine (Zeilenumbruch, neuer String [] {a}, neue Ganzzahl [] {b});
Elad Benda
3
@ user311130 Ihr Code ist schwer zu lesen, aber Sie könnten eine neue Frage erstellen, die etwa lautet: "Ich habe diese Antwort gefunden <Link zu meiner Antwort>, aber die folgende funktioniert nicht <Ihr Code hier>
OscarRyz
Das ist böse, aber ich gebe dem immer noch ein "Gefällt mir", weil es das Problem auf eine "sauberste" Weise löst, die ich mir für Java in diesem Moment
vorstellen
8

Soweit ich weiß, gibt es in der Java- Sprache weder ein ref noch ein out- Schlüsselwortäquivalent . Ich habe jedoch gerade einen C # -Code in Java umgewandelt , der out- Parameter verwendet und berät, was ich gerade getan habe. Sie sollten jedes Objekt in eine Wrapper-Klasse einschließen und die in der Wrapper-Objektinstanz eingeschlossenen Werte wie folgt übergeben.

Ein einfaches Beispiel für die Verwendung von Wrapper

Hier ist die Wrapper-Klasse ;

public class Wrapper {
    public Object ref1; // use this as ref
    public Object ref2; // use this as out

    public Wrapper(Object ref1) {
        this.ref1 = ref1;
    }
}

Und hier ist der Testcode;

public class Test {

    public static void main(String[] args) {
        String abc = "abc";
        changeString(abc);
        System.out.println("Initial object: " + abc); //wont print "def"

        Wrapper w = new Wrapper(abc);
        changeStringWithWrapper(w);
        System.out.println("Updated object: " + w.ref1);
        System.out.println("Out     object: " + w.ref2);
    }

    // This won't work
    public static void changeString(String str) {
        str = "def";
    }

    // This will work
    public static void changeStringWithWrapper(Wrapper w) {
        w.ref1 = "def";
        w.ref2 = "And this should be used as out!";
    }

}

Ein Beispiel aus der realen Welt

AC # .NET-Methode mit out-Parameter

Hier gibt es eine C # .NET- Methode, die das Schlüsselwort out verwendet .

public bool Contains(T value)
{
    BinaryTreeNode<T> parent;
    return FindWithParent(value, out parent) != null;
}

private BinaryTreeNode<T> FindWithParent(T value, out BinaryTreeNode<T> parent)
{
    BinaryTreeNode<T> current = _head;
    parent = null;

    while(current != null)
    {
        int result = current.CompareTo(value);

        if (result > 0)
        {
            parent = current;
            current = current.Left;
        }
        else if (result < 0)
        {
            parent = current;
            current = current.Right;
        }
        else
        {
            break;
        }
    }

    return current;
}

Java Entspricht dem C # -Code, der den Parameter out verwendet

Das Java- Äquivalent dieser Methode mit Hilfe der Wrapper-Klasse lautet wie folgt:

public boolean contains(T value) {
    BinaryTreeNodeGeneration<T> result = findWithParent(value);

    return (result != null);
}

private BinaryTreeNodeGeneration<T> findWithParent(T value) {
    BinaryTreeNode<T> current = head;
    BinaryTreeNode<T> parent = null;
    BinaryTreeNodeGeneration<T> resultGeneration = new BinaryTreeNodeGeneration<T>();
    resultGeneration.setParentNode(null);

    while(current != null) {
        int result = current.compareTo(value);

        if(result >0) {
            parent = current;
            current = current.left;
        } else if(result < 0) {
            parent = current;
            current = current.right;
        } else {
            break;
        }
    }

    resultGeneration.setChildNode(current);
    resultGeneration.setParentNode(parent);

    return resultGeneration;
}

Wrapper-Klasse

Die in diesem Java-Code verwendete Wrapper-Klasse lautet wie folgt.

public class BinaryTreeNodeGeneration<TNode extends Comparable<TNode>>  {

    private BinaryTreeNode<TNode>   parentNode;
    private BinaryTreeNode<TNode>   childNode;

    public BinaryTreeNodeGeneration() {
        this.parentNode = null;
        this.childNode = null;
    }

    public BinaryTreeNode<TNode> getParentNode() {
        return parentNode;
    }

    public void setParentNode(BinaryTreeNode<TNode> parentNode) {
        this.parentNode = parentNode;
    }

    public BinaryTreeNode<TNode> getChildNode() {
        return childNode;
    }

    public void setChildNode(BinaryTreeNode<TNode> childNode) {
        this.childNode = childNode;
    }

}
Levent Divilioglu
quelle
siehe frühere Antworten.
Pashute
Eine detaillierte Antwort negativ zu bewerten ist lustig. Ich habe SO ausgecheckt und konnte keine genaue Antwort mit Demo-Code finden. Deshalb habe ich diese Antwort geschrieben, soweit ich mich erinnern kann. Wenn Sie auf eine einzelne Antwort warten und jede andere Antwort ablehnen, sollte SO alle anderen Antworten verbieten, die vom Eigentümer der Frage bestätigt wurden.
Levent Divilioglu
OK, ich kann die negative Abstimmung nur entfernen, wenn Sie die Antwort bearbeiten. Lesen Sie also die anderen Antworten und erklären Sie, warum Sie diese Lösungen nicht verwenden wollten. Ein Wrapper (und insbesondere ein REF- und OUT-Wrapper, nur zum Spaß). 5 Personen gaben kurze und vollständige Antworten mit Beispielen , und nur Eyal schrieb im Grunde: "Nein, kannst du nicht".
Pashute
1
Diese Antwort sieht gut aus. Es ähnelt der Top-Antwort, enthält jedoch ausführliche Beispiele zur Verwendung einer Wrapper-Klasse in Java.
Hubatish
8

Java übergibt Parameter als Wert und verfügt über keinen Mechanismus, um die Referenzübergabe zuzulassen. Das heißt, wenn ein Parameter übergeben wird, wird sein Wert in den Stapelrahmen kopiert, der den Aufruf verarbeitet.

Der Begriff Wert, wie ich ihn hier verwende, bedarf einer kleinen Klärung. In Java gibt es zwei Arten von Variablen - Grundelemente und Objekte. Ein Wert eines Grundelements ist das Grundelement selbst, und der Wert eines Objekts ist seine Referenz (und nicht der Zustand des Objekts, auf das verwiesen wird). Daher ändert jede Änderung des Werts innerhalb der Methode nur die Kopie des Werts im Stapel und wird vom Aufrufer nicht gesehen. Zum Beispiel gibt es keine Möglichkeit, eine echte Swap-Methode zu implementieren, die zwei Referenzen empfängt und diese austauscht (nicht ihren Inhalt!).

Eyal Schneider
quelle
6

Wie viele andere musste ich ein C # -Projekt nach Java konvertieren. Ich habe nicht eine komplette Lösung , die im Internet in Bezug auf finden heraus und ref Modifikatoren. Aber ich konnte die Informationen, die ich gefunden hatte, nutzen und erweitern, um meine eigenen Klassen zu erstellen, um die Anforderungen zu erfüllen. Ich wollte aus Gründen der Codeklarheit zwischen ref- und out- Parametern unterscheiden. Mit den folgenden Klassen ist es möglich. Mögen diese Informationen anderen Zeit und Mühe sparen.

Ein Beispiel ist im folgenden Code enthalten.

//*******************************************************************************************
//XOUT CLASS
//*******************************************************************************************
public class XOUT<T>
{
    public XOBJ<T> Obj = null;

    public XOUT(T value)
    {
        Obj = new XOBJ<T>(value);
    }

    public XOUT()
    {
      Obj = new XOBJ<T>();
    }

    public XOUT<T> Out()
    {
        return(this);
    }

    public XREF<T> Ref()
    {
        return(Obj.Ref());
    }
};

//*******************************************************************************************
//XREF CLASS
//*******************************************************************************************

public class XREF<T>
{
    public XOBJ<T> Obj = null;

    public XREF(T value)
    {
        Obj = new XOBJ<T>(value);
    }

    public XREF()
    {
      Obj = new XOBJ<T>();
    }

    public XOUT<T> Out()
    {
        return(Obj.Out());
    }

    public XREF<T> Ref()
    {
        return(this);
    }
};

//*******************************************************************************************
//XOBJ CLASS
//*******************************************************************************************
/**
 *
 * @author jsimms
 */
/*
    XOBJ is the base object that houses the value. XREF and XOUT are classes that
    internally use XOBJ. The classes XOBJ, XREF, and XOUT have methods that allow
    the object to be used as XREF or XOUT parameter; This is important, because
    objects of these types are interchangeable.

    See Method:
       XXX.Ref()
       XXX.Out()

    The below example shows how to use XOBJ, XREF, and XOUT;
    //
    // Reference parameter example
    //
    void AddToTotal(int a, XREF<Integer> Total)
    {
       Total.Obj.Value += a;
    }

    //
    // out parameter example
    //
    void Add(int a, int b, XOUT<Integer> ParmOut)
    {
       ParmOut.Obj.Value = a+b;
    }

    //
    // XOBJ example
    //
    int XObjTest()
    {
       XOBJ<Integer> Total = new XOBJ<>(0);
       Add(1, 2, Total.Out());    // Example of using out parameter
       AddToTotal(1,Total.Ref()); // Example of using ref parameter
       return(Total.Value);
    }
*/


public class XOBJ<T> {

    public T Value;

    public  XOBJ() {

    }

    public XOBJ(T value) {
        this.Value = value;
    }

    //
    // Method: Ref()
    // Purpose: returns a Reference Parameter object using the XOBJ value
    //
    public XREF<T> Ref()
    {
        XREF<T> ref = new XREF<T>();
        ref.Obj = this;
        return(ref);
    }

    //
    // Method: Out()
    // Purpose: returns an Out Parameter Object using the XOBJ value
    //
    public XOUT<T> Out()
    {
        XOUT<T> out = new XOUT<T>();
        out.Obj = this;
        return(out);
    }

    //
    // Method get()
    // Purpose: returns the value
    // Note: Because this is combersome to edit in the code,
    // the Value object has been made public
    //
    public T get() {
        return Value;
    }

    //
    // Method get()
    // Purpose: sets the value
    // Note: Because this is combersome to edit in the code,
    // the Value object has been made public
    //
    public void set(T anotherValue) {
        Value = anotherValue;
    }

    @Override
    public String toString() {
        return Value.toString();
    }

    @Override
    public boolean equals(Object obj) {
        return Value.equals(obj);
    }

    @Override
    public int hashCode() {
        return Value.hashCode();
    }
}
James W Simms
quelle
-1

Java hat keine Standardmethode. Die meisten Swaps werden auf der Liste durchgeführt, die in der Klasse verpackt ist. aber es gibt einen inoffiziellen Weg, dies zu tun:

package Example;

import java.lang.reflect.Field;
import java.util.logging.Level;
import java.util.logging.Logger;



 public class Test{


private static <T> void SetValue(T obj,T value){
    try {
        Field f = obj.getClass().getDeclaredField("value");
        f.setAccessible(true);
        f.set(obj,value);
        } catch (IllegalAccessException | IllegalArgumentException | 
            NoSuchFieldException | SecurityException ex) {
            Logger.getLogger(CautrucjavaCanBan.class.getName()).log(Level.SEVERE, 
       null, ex);
        }
}
private  static  void permutation(Integer a,Integer b){
    Integer tmp = new Integer(a);
    SetValue(a, b);
    SetValue(b, tmp);
}
 private  static  void permutation(String a,String b){
    char[] tmp = a.toCharArray();
    SetValue(a, b.toCharArray());
    SetValue(b, tmp);
}
public static void main(String[] args) {
    {
        Integer d = 9;
        Integer e = 8;
        HoanVi(d, e);
        System.out.println(d+" "+ e);
    }
    {
        String d = "tai nguyen";
        String e = "Thai nguyen";
        permutation(d, e);
        System.out.println(d+" "+ e);
    }
}

}
Tai Nguyen
quelle
1
Das gibt Ihnen keine Referenzsemantik für einen Parameter, sondern nur Reflexion, um ein Objekt zu mutieren, das unveränderlich gestaltet ist. Dies ist eine schreckliche Idee und bietet immer noch keine Referenzsemantik für den Parameter.
Servy