Java ha qualcosa come le parole chiave ref e out di C #?


113

Qualcosa di simile al seguente:

esempio di riferimento:

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

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

fuori esempio:

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

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


5
IMO non ti stai perdendo molto. L'unica volta che uso refo outin C # è quando sto usando un pattern come TryParse(), dove il metodo restituisce un risultato booleano e l'unico modo per ottenere un valore analizzato è usando refo out.
Robert Harvey

3
Indovina un po ', è proprio quello che devo usare! ;)
elisio divorato

L'altro modo per farlo è restituire un oggetto composto con sia lo stato che un valore nullable in esso. Ma ammetto che sia un po 'Rube Goldberg-ish.
Robert Harvey

1
Niente di sbagliato nel restituire un oggetto composto, se solo ce ne fosse uno utilizzabile predefinito (es. Tuple). Ma aspetta, ciò avrebbe bisogno di generici non cancellati che lavorino con tipi primitivi per essere efficiente :)
Pavel Minaev

Risposte:


102

No, Java non ha qualcosa come C # refe outparole chiave per il passaggio per riferimento.

Puoi passare solo per valore in Java. Anche i riferimenti vengono passati per valore. Vedere la pagina di Jon Skeet sul passaggio di parametri in Java per maggiori dettagli.

Per fare qualcosa di simile a refo outdovresti avvolgere i tuoi parametri all'interno di un altro oggetto e passare quel riferimento all'oggetto come parametro.


5
Questo dovrebbe essere ampliato su alcuni. Puoi passare solo primitive (int, short, char, ecc.) Come valore. E no, non c'è fuori.
Corey Sunwold

14
Questo non è vero al 100%, se si passa in un array o in una classe il riferimento all'array o all'oggetto viene passato per valore, è possibile modificare gli interni all'array o all'oggetto e si rifletterà nel chiamante.
Romain Hippeau

12
@fearofawhackplanet: Um, a meno che tu non usi ref.
Robert Harvey

2
@fearofawhackplanet: 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
Dal punto di vista di CLR, stai passando un riferimento gestito ( T&) per valore, sì. Ma C # ha una sua terminologia e specificamente non include cose come valori di tipo ref T- dal punto di vista di C #, refè strettamente un modificatore di parametri e parlare di "passaggio di un riferimento per valore" rispetto ad esso non ha senso .
Pavel Minaev

30

Risposta diretta: no

Ma puoi simulare il riferimento con i wrapper .

E fai quanto segue:

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

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

Su

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

E fondamentalmente qualsiasi altro tipo come:

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

out.println( one ); // May print 2

28
Ahia. È brutto.
elisio divorato

46
Non ho mai detto, puoi farlo in questo modo elegante : P
OscarRyz

quindi perché quanto segue non funziona: private static void ParseLine (String newline, String [] aWrapper, Integer [] bWrapper) {StringTokenizer st = new StringTokenizer (newline); aWrapper [0] = st.nextToken (); bWrapper [0] = new Integer (st.nextToken ()); } ParseLine (newline, new String [] {a}, new Integer [] {b});
Elad Benda

3
@ user311130 Il tuo codice è difficile da leggere, ma potresti creare una nuova domanda dicendo qualcosa del tipo: "Ho trovato questa risposta <link alla mia risposta> ma quanto segue non funziona <il tuo codice qui>
OscarRyz

È sgradevole, ma lo sto ancora dando perché risolve il problema in un modo "più pulito" che posso pensare per Java in questo momento ...
Pangamma

8

In realtà non ci sono né refout parole chiave equivalenti nel linguaggio Java per quanto ne so. Tuttavia ho appena trasformato un codice C # in Java che utilizza il parametro out e consiglierà ciò che ho appena fatto. Dovresti racchiudere qualsiasi oggetto in una classe wrapper e passare i valori racchiusi nell'istanza dell'oggetto wrapper come segue;

Un semplice esempio per l'utilizzo di wrapper

Ecco la classe Wrapper ;

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

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

Ed ecco il codice di prova;

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

}

Un esempio del mondo reale

Metodo AC # .NET che utilizza il parametro out

Qui c'è un metodo C # .NET che utilizza la parola chiave out ;

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 Equivalente del codice C # che utilizza il parametro out

E l' equivalente Java di questo metodo con l'aiuto della classe wrapper è il seguente;

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

Classe wrapper

E la classe wrapper utilizzata in questo codice Java è la seguente;

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

}

vedere le risposte precedenti.
pashute

Dare una risposta dettagliata con un voto negativo è divertente. Ho verificato SO e non sono riuscito a trovare una risposta esatta con il codice demo, ecco perché ho scritto questa risposta per quanto posso ricordare. Se aspetti una singola risposta e decidi di votare ogni altra risposta, SO dovrebbe bandire tutte le risposte diverse da quella che ha ricevuto conferma dal proprietario della domanda.
Levent Divilioglu

OK, posso rimuovere il voto negativo solo se modifichi la risposta. Quindi leggi le altre risposte e spiega perché non hai voluto utilizzare quelle soluzioni. Un wrapper (e in particolare un REF e OUT wrapper solo per il gusto di farlo). 5 persone hanno fornito risposte brevi e complete con esempi , e solo Eyal ha sostanzialmente scritto: "No, non puoi".
pashute

1
Questa risposta sembra a posto. È simile alla risposta principale, ma fornisce esempi completamente dettagliati su come utilizzare una classe wrapper in Java.
hubatish

8

Java passa i parametri per valore e non dispone di alcun meccanismo per consentire il passaggio per riferimento. Ciò significa che ogni volta che viene passato un parametro, il suo valore viene copiato nello stack frame che gestisce la chiamata.

Il termine valore come lo uso qui necessita di un piccolo chiarimento. In Java abbiamo due tipi di variabili: primitive e oggetti. Un valore di una primitiva è la primitiva stessa e il valore di un oggetto è il suo riferimento (e non lo stato dell'oggetto a cui si fa riferimento). Pertanto, qualsiasi modifica al valore all'interno del metodo cambierà solo la copia del valore nello stack e non sarà vista dal chiamante. Ad esempio, non c'è alcun modo per implementare un vero metodo di scambio, che riceve due riferimenti e li scambia (non il loro contenuto!).


6

Come molti altri, avevo bisogno di convertire un progetto C # in Java. Non ho trovato una soluzione completa sul web per quanto riguarda i modificatori di out e ref . Ma sono stato in grado di prendere le informazioni che ho trovato e di espanderle per creare le mie classi per soddisfare i requisiti. Volevo fare una distinzione tra i parametri ref e out per la chiarezza del codice. Con le classi sottostanti, è possibile. Possano queste informazioni risparmiare tempo e fatica.

Un esempio è incluso nel codice sottostante.

//*******************************************************************************************
//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();
    }
}


-1

java non ha un modo standard per farlo. La maggior parte degli scambi verrà effettuata nell'elenco che è impacchettato nella classe. ma c'è un modo non ufficiale per farlo:

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

}

1
Questo non ti dà la semantica di riferimento per un parametro, ma usa solo la riflessione per mutare un oggetto progettato per essere immutabile, che è un'idea terribile e ancora non fornisce la semantica di riferimento per il parametro.
Servizio
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.