¿Java tiene algo como las palabras clave ref y out de C #?

113

Algo parecido a lo siguiente:

ejemplo de referencia:

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

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

ejemplo:

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

void main() {
    String abc;
    changeString(out abc);
    System.out.println(abc); //prints "def"
}
elisio devorado
fuente
1
posible duplicado de ¿Puedo pasar parámetros por referencia en Java?
Robert Harvey
5
En mi opinión, no te estás perdiendo mucho. La única vez que uso refo outen C # es cuando estoy usando un patrón como TryParse(), donde el método devuelve un resultado booleano, y la única forma de obtener un valor analizado es usando refo out.
Robert Harvey
3
Adivina qué, eso es lo que necesito usar. ;)
devoró elisio
La otra forma de hacerlo es devolver un objeto compuesto con el estado y un valor anulable en él. Pero admito que es un poco Rube Goldberg.
Robert Harvey
1
No hay nada de malo en devolver un objeto compuesto, si solo hubiera uno predefinido utilizable (es decir, tuplas). Pero espere, eso necesitaría genéricos no borrados que trabajen con tipos primitivos para ser eficientes :)
Pavel Minaev

Respuestas:

102

No, Java no tiene algo como C # refy outpalabras clave para pasar por referencia.

Solo puede pasar por valor en Java. Incluso las referencias se pasan por valor. Consulte la página de Jon Skeet sobre el paso de parámetros en Java para obtener más detalles.

Para hacer algo similar a refo outtendría que envolver sus parámetros dentro de otro objeto y pasar esa referencia de objeto como parámetro.

Mark Byers
fuente
5
Esto debería ampliarse en algunos. Solo puede pasar primitivas (int, short, char, etc.) como valor. Y no, no hay salida.
Corey Sunwold
14
Eso no es 100% cierto. Si pasa una matriz o una clase, la referencia a la matriz u objeto se pasa por valor, puede cambiar los componentes internos de la matriz u objeto y se reflejará en el llamador.
Romain Hippeau
12
@fearofawhackplanet: Um, a menos que uses 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
Desde la perspectiva de CLR, está pasando una referencia administrada ( T&) por valor, sí. Pero C # tiene su propia terminología, y específicamente no incluye cosas como valores de tipo ref T; desde la perspectiva de C #, refes estrictamente un modificador de parámetro, y hablar de "pasar una referencia por valor" con respecto a él no tiene sentido .
Pavel Minaev
30

Respuesta directa: No

Pero puede simular referencias con envoltorios .

Y haz lo siguiente:

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

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

Fuera

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

Y básicamente cualquier otro tipo como:

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

out.println( one ); // May print 2
OscarRyz
fuente
28
Ay. Eso es feo.
Devoró elisio
46
Nunca dije, puedes hacerlo de esta manera elegante : P
OscarRyz
entonces, ¿por qué no funciona lo siguiente? private static void ParseLine (String newline, String [] aWrapper, Integer [] bWrapper) {StringTokenizer st = new StringTokenizer (newline); aWrapper [0] = st.nextToken (); bWrapper [0] = nuevo entero (st.nextToken ()); } ParseLine (nueva línea, nueva cadena [] {a}, nuevo entero [] {b});
Elad Benda
3
@ user311130 Su código es difícil de leer, pero podría crear una nueva pregunta que diga algo como: "Encontré esta respuesta <enlace a mi respuesta> pero lo siguiente no funciona <su código aquí>
OscarRyz
Eso es desagradable, pero todavía le doy un me gusta a esto porque resuelve el problema de una manera "más limpia" en la que puedo pensar para Java en este momento ...
Pangamma
8

En realidad, no hay ni ref ni un equivalente de palabra clave en el lenguaje Java que yo sepa. Sin embargo, acabo de transformar un código C # en Java que usa nuestro parámetro y le aconsejaré lo que acabo de hacer. Debe envolver cualquier objeto en una clase contenedora y pasar los valores envueltos en la instancia del objeto contenedor de la siguiente manera;

Un ejemplo simple para usar Wrapper

Aquí está la clase 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;
    }
}

Y aquí está el código de prueba;

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 ejemplo del mundo real

Método AC # .NET usando el parámetro out

Aquí hay un método de C # .NET que usa nuestra palabra clave;

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 código C # que usa el parámetro out

Y el equivalente en Java de este método con la ayuda de la clase contenedora es el siguiente;

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

Clase de envoltura

Y la clase contenedora utilizada en este código Java es la siguiente;

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
fuente
ver respuestas anteriores.
pashute
Dar una respuesta detallada con un voto negativo es divertido. Revisé SO y no pude encontrar una buena respuesta exacta con el código de demostración, es por eso que escribí esta respuesta hasta donde puedo recordar. Si espera una respuesta única y rechaza cualquier otra respuesta, entonces SO debería prohibir todas las respuestas que no sean las que recibieron una confirmación del propietario de la pregunta.
Levent Divilioglu,
De acuerdo, puedo eliminar el voto negativo solo si editas la respuesta. Así que lea las otras respuestas y explique por qué no quiso utilizar esas soluciones. Un envoltorio (y en particular un envoltorio REF y OUT solo por diversión). 5 personas dieron respuestas cortas y completas con ejemplos , y solo Eyal escribió básicamente: "No, no puedes".
pashute
1
Esta respuesta se ve bien. Es similar a la respuesta principal, pero brinda ejemplos completamente detallados sobre cómo usar una clase contenedora en Java.
hubatish
8

Java pasa los parámetros por valor y no tiene ningún mecanismo para permitir el paso por referencia. Eso significa que cada vez que se pasa un parámetro, su valor se copia en el marco de pila que maneja la llamada.

El término valor tal como lo uso aquí necesita una pequeña aclaración. En Java tenemos dos tipos de variables: primitivas y objetos. Un valor de una primitiva es la propia primitiva, y el valor de un objeto es su referencia (y no el estado del objeto al que se hace referencia). Por lo tanto, cualquier cambio en el valor dentro del método solo cambiará la copia del valor en la pila y no será visto por la persona que llama. Por ejemplo, no hay forma de implementar un método de intercambio real, que reciba dos referencias y las intercambie (¡no su contenido!).

Eyal Schneider
fuente
6

Como muchos otros, necesitaba convertir un proyecto de C # a Java. No encontré una solución completa en la web con respecto a los modificadores de salida y referencia . Pero pude tomar la información que encontré y ampliarla para crear mis propias clases para cumplir con los requisitos. Quería hacer una distinción entre los parámetros ref y out para la claridad del código. Con las clases siguientes, es posible. Que esta información ahorre tiempo y esfuerzo a otros.

Se incluye un ejemplo en el código siguiente.

//*******************************************************************************************
//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
fuente
-1

java no tiene una forma estándar de hacerlo. La mayoría de los intercambios se realizarán en la lista empaquetada en la clase. pero hay una forma no oficial de hacerlo:

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
fuente
1
Eso no le da semántica de referencia para un parámetro, es solo usar la reflexión para mutar un objeto que está diseñado para ser inmutable, lo cual es una idea terrible y aún no proporciona semántica de referencia para el parámetro.
Servicio