Java a-t-il quelque chose comme les mots-clés ref and out de C #?

113

Quelque chose comme ce qui suit:

exemple ref:

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

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

out exemple:

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

void main() {
    String abc;
    changeString(out abc);
    System.out.println(abc); //prints "def"
}
dévoré d'elysium
la source
1
duplication possible de Puis-je passer des paramètres par référence en Java?
Robert Harvey
5
OMI vous ne manquez pas grand-chose. La seule fois où j'utilise refou outen C #, c'est lorsque j'utilise un modèle comme TryParse(), où la méthode renvoie un résultat booléen, et la seule façon d'en obtenir une valeur analysée est d'utiliser refou out.
Robert Harvey
3
Devinez quoi, c'est juste ce que j'ai besoin d'utiliser! ;)
elysium dévoré
L'autre façon de le faire est de renvoyer un objet composite avec à la fois l'état et une valeur Nullable. Mais j'avoue que c'est un peu Rube Goldberg-ish.
Robert Harvey
1
Rien de mal à renvoyer un objet composite, si seulement il y en aurait un utilisable prédéfini (ie des tuples). Mais attendez, cela aurait besoin de génériques non effacés fonctionnant avec des types primitifs pour être efficaces :)
Pavel Minaev

Réponses:

102

Non, Java n'a pas quelque chose comme C # refetout mots clés pour passer par référence.

Vous ne pouvez passer que par valeur en Java. Même les références sont passées par valeur. Voir la page de Jon Skeet sur la transmission de paramètres en Java pour plus de détails.

Pour faire quelque chose de similaire à refou, outvous devez envelopper vos paramètres dans un autre objet et passer cette référence d'objet en tant que paramètre.

Mark Byers
la source
5
Cela devrait être développé sur certains. Vous ne pouvez passer que des primitives (int, short, char, etc.) comme valeur. Et non, il n'y a pas de sortie.
Corey Sunwold
14
Ce n'est pas vrai à 100%. Si vous passez dans un tableau ou une classe, la référence au tableau ou à l'objet est transmise par valeur, vous pouvez modifier les éléments internes du tableau ou de l'objet et cela sera reflété dans l'appelant.
Romain Hippeau
12
@fearofawhackplanet: Hum, sauf si vous utilisez 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
Du point de vue CLR, vous passez une référence gérée ( T&) par valeur, oui. Mais C # a sa propre terminologie, et il n'inclut pas spécifiquement des choses telles que les valeurs de type ref T- du point de vue C #, refest strictement un modificateur de paramètre, et parler de "passer une référence par valeur" par rapport à cela n'a pas de sens .
Pavel Minaev
30

Réponse directe: non

Mais vous pouvez simuler une référence avec des wrappers .

Et procédez comme suit:

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

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

En dehors

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

Et fondamentalement tout autre type tel que:

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

out.println( one ); // May print 2
OscarRyz
la source
28
Aie. C'est moche.
dévoré l'elysium
46
Je n'ai jamais dit, vous pouvez le faire de cette manière élégante : P
OscarRyz
alors pourquoi ce qui suit ne fonctionne pas: 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 du
3
@ user311130 Votre code est difficile à lire, mais vous pouvez créer une nouvelle question en disant quelque chose comme: "J'ai trouvé cette réponse <lien vers ma réponse> mais ce qui suit ne fonctionne pas <votre code ici>
OscarRyz
C'est méchant, mais je donne toujours ceci parce que cela résout le problème de la manière la plus "propre" à laquelle je puisse penser pour Java en ce moment ...
Pangamma
8

En fait, il n'y a ni ref ni out équivalent mot-clé en langage Java pour autant que je sache. Cependant, je viens de transformer un code C # en Java qui utilise un paramètre et vous conseillera ce que je viens de faire. Vous devez envelopper n'importe quel objet dans une classe wrapper et transmettre les valeurs encapsulées dans l'instance d'objet wrapper comme suit;

Un exemple simple d'utilisation de Wrapper

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

Et voici le code de test;

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 exemple du monde réel

Méthode AC # .NET utilisant le paramètre out

Ici, il existe une méthode C # .NET qui utilise le mot clé 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 Equivalent du code C # utilisant le paramètre out

Et l' équivalent Java de cette méthode à l'aide de la classe wrapper est le suivant;

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

Et la classe wrapper utilisée dans ce code Java est comme ci-dessous;

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
la source
voir les anciennes réponses.
pashute
Donner une réponse détaillée à un vote négatif est amusant. J'ai vérifié SO et je n'ai pas trouvé de réponse exacte avec un code de démonstration, c'est pourquoi j'ai écrit cette réponse d'aussi loin que je me souvienne. Si vous attendez une seule réponse et votez contre toutes les autres réponses, alors SO devrait interdire toutes les réponses autres que celles qui ont obtenu une confirmation du propriétaire de la question.
Levent Divilioglu
OK, je ne peux supprimer le vote négatif que si vous modifiez la réponse. Alors lisez les autres réponses et expliquez pourquoi vous ne vouliez pas utiliser ces solutions. Un wrapper (et en particulier un wrapper REF et OUT juste pour le plaisir). 5 personnes ont donné des réponses courtes et complètes avec des exemples , et seul Eyal a essentiellement écrit: "Non, vous ne pouvez pas".
pashute le
1
Cette réponse semble bien. Elle est similaire à la réponse principale, mais donne des exemples entièrement détaillés sur la façon d'utiliser une classe wrapper en Java.
hubatish
8

Java passe les paramètres par valeur et ne dispose d'aucun mécanisme pour autoriser le passage par référence. Cela signifie que chaque fois qu'un paramètre est passé, sa valeur est copiée dans le cadre de pile qui gère l'appel.

Le terme valeur tel que je l'utilise ici a besoin d'un peu de clarification. En Java, nous avons deux types de variables: les primitives et les objets. Une valeur d'une primitive est la primitive elle-même, et la valeur d'un objet est sa référence (et non l'état de l'objet référencé). Par conséquent, toute modification de la valeur à l'intérieur de la méthode changera uniquement la copie de la valeur dans la pile et ne sera pas vue par l'appelant. Par exemple, il n'y a aucun moyen d'implémenter une véritable méthode d'échange, qui reçoit deux références et les échange (pas leur contenu!).

Eyal Schneider
la source
6

Comme beaucoup d'autres, j'avais besoin de convertir un projet C # en Java. Je n'ai pas trouvé de solution complète sur le Web concernant les modificateurs out et ref . Mais j'ai pu prendre les informations que j'ai trouvées et les développer pour créer mes propres classes afin de répondre aux exigences. Je voulais faire une distinction entre les paramètres ref et out pour la clarté du code. Avec les classes ci-dessous, c'est possible. Puisse cette information faire gagner du temps et des efforts aux autres.

Un exemple est inclus dans le code ci-dessous.

//*******************************************************************************************
//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
la source
-1

java n'a pas de méthode standard pour le faire. La plupart des échanges seront effectués sur la liste qui est emballée dans la classe. mais il existe un moyen non officiel de le faire:

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
la source
1
Cela ne vous donne pas une sémantique de référence pour un paramètre, c'est simplement utiliser la réflexion pour muter un objet conçu pour être immuable, ce qui est une idée terrible , et ne fournit toujours pas de sémantique de référence pour le paramètre.
Servy