O Java tem algo como palavras-chave ref e out do C #?

113

Algo como o seguinte:

exemplo ref:

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

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

nosso exemplo:

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

void main() {
    String abc;
    changeString(out abc);
    System.out.println(abc); //prints "def"
}
elísio devorado
fonte
1
possível duplicata de Posso passar parâmetros por referência em Java?
Robert Harvey
5
IMO você não está perdendo muito. A única vez que eu uso refou outem C # é quando estou usando um padrão como TryParse(), onde o método retorna um resultado booleano e a única maneira de obter um valor analisado dele é usando refou out.
Robert Harvey
3
Adivinha, isso é o que eu preciso usar! ;)
devorado elysium
A outra maneira de fazer isso é retornar um objeto de composição com o status e um valor anulável nele. Mas eu admito que é um pouco Rube Goldberg.
Robert Harvey
1
Nada de errado em retornar um objeto composto, se apenas houvesse um utilizável predefinido (ou seja, tuplas). Mas espere, isso precisaria de genéricos não apagados trabalhando com tipos primitivos para ser eficiente :)
Pavel Minaev

Respostas:

102

Não, Java não tem algo como C # refe outpalavras - chave para passar por referência.

Você só pode passar por valor em Java. Mesmo as referências são passadas por valor. Veja a página de Jon Skeet sobre passagem de parâmetros em Java para mais detalhes.

Para fazer algo semelhante a refou, outvocê teria que envolver seus parâmetros dentro de outro objeto e passar essa referência de objeto como um parâmetro.

Mark Byers
fonte
5
Isso deve ser expandido em alguns. Você só pode passar primitivos (int, short, char, etc.) como valor. E não, não há saída.
Corey Sunwold
14
Isso não é 100% verdadeiro. Se você passar uma matriz ou uma classe, a referência à matriz ou objeto é passada por valor, você pode alterar os elementos internos da matriz ou objeto e isso será refletido no chamador.
Romain Hippeau
12
@fearofawhackplanet: Hum, a menos que você use 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
Da perspectiva do CLR, você está passando uma referência gerenciada ( T&) por valor, sim. Mas C # tem sua própria terminologia e especificamente não inclui coisas como valores de tipo ref T- da perspectiva do C #, refé estritamente um modificador de parâmetro e falar em "passar uma referência por valor" em relação a ele não faz sentido .
Pavel Minaev
30

Resposta direta: não

Mas você pode simular a referência com wrappers .

E faça o seguinte:

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

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

Fora

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

E basicamente qualquer outro tipo, como:

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

out.println( one ); // May print 2
OscarRyz
fonte
28
Ai. Isso é feio.
Elysium devorado
46
Eu nunca disse, você pode fazer desta maneira elegante : P
OscarRyz
então, por que o seguinte não funciona: private static void ParseLine (String newline, String [] aWrapper, Integer [] bWrapper) {StringTokenizer st = new StringTokenizer (newline); aWrapper [0] = st.nextToken (); bWrapper [0] = novo Inteiro (st.nextToken ()); } ParseLine (nova linha, nova String [] {a}, novo Inteiro [] {b});
Elad Benda
3
@ user311130 Seu código é difícil de ler, mas você poderia criar uma nova pergunta dizendo algo como: "Encontrei esta resposta <link para minha resposta>, mas o seguinte não funciona <seu código aqui>
OscarRyz
Isso é desagradável, mas ainda estou gostando porque resolve o problema de uma forma "mais limpa" que eu posso pensar para Java neste momento ...
Pangamma
8

Na verdade, não há nem ref nem palavra - chave out equivalente na linguagem Java , até onde eu sei. No entanto, acabei de transformar um código C # em Java que usa o parâmetro out e aconselhará o que acabei de fazer. Você deve envolver qualquer objeto em uma classe de invólucro e passar os valores agrupados na instância do objeto de invólucro da seguinte forma;

Um exemplo simples para usar Wrapper

Aqui está a 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;
    }
}

E aqui está o código de teste;

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

}

Um exemplo do mundo real

Método AC # .NET usando parâmetro out

Aqui há um método C # .NET que está usando a palavra - chave 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 ao código C # que está usando o parâmetro out

E o equivalente em Java deste método com a ajuda da classe wrapper é o seguinte;

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 a classe de wrapper usada neste código Java é a seguinte;

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
fonte
veja as respostas anteriores.
pashute de
Dar uma resposta detalhada com um voto negativo é engraçado. Eu verifiquei o SO e não consegui encontrar uma resposta exata com o código de demonstração, é por isso que escrevi esta resposta até onde me lembro. Se você esperar por uma única resposta e votar negativamente em todas as outras, o SO deve banir todas as respostas que não tenham recebido a confirmação do proprietário da questão.
Levent Divilioglu
OK, só posso remover o voto negativo se você editar a resposta. Portanto, leia as outras respostas e explique por que você não quis usar essas soluções. Um invólucro (e em particular um invólucro REF e OUT apenas por diversão). 5 pessoas deram respostas curtas e completas com exemplos , e apenas Eyal basicamente escreveu: "Não, você não pode".
pashute
1
Esta resposta parece boa. É semelhante à resposta principal, mas fornece exemplos totalmente detalhados sobre como usar uma classe de wrapper em Java.
hubatish de
8

Java passa parâmetros por valor e não possui nenhum mecanismo para permitir a passagem por referência. Isso significa que sempre que um parâmetro é passado, seu valor é copiado para o quadro de pilha que trata da chamada.

O termo valor, conforme o uso aqui, precisa de um pequeno esclarecimento. Em Java, temos dois tipos de variáveis ​​- primitivas e objetos. Um valor de uma primitiva é a própria primitiva, e o valor de um objeto é sua referência (e não o estado do objeto que está sendo referenciado). Portanto, qualquer alteração no valor dentro do método alterará apenas a cópia do valor na pilha e não será vista pelo chamador. Por exemplo, não há como implementar um método de troca real, que recebe duas referências e as troca (não seu conteúdo!).

Eyal Schneider
fonte
6

Como muitos outros, precisei converter um projeto C # para Java. Não encontrei uma solução completa na web em relação aos modificadores out e ref . Mas consegui pegar as informações que encontrei e expandi-las para criar minhas próprias classes para atender aos requisitos. Eu queria fazer uma distinção entre os parâmetros ref e out para clareza de código. Com as classes abaixo, é possível. Que esta informação economize tempo e esforço de outras pessoas.

Um exemplo está incluído no código abaixo.

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

java não tem uma maneira padrão de fazer isso. A maioria das trocas será feita na lista que é fornecida na aula. mas existe uma maneira não oficial de fazer isso:

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
fonte
1
Isso não está fornecendo semântica de referência para um parâmetro, mas apenas usando reflexão para transformar um objeto que foi projetado para ser imutável, o que é uma ideia terrível e ainda não fornece semântica de referência para o parâmetro.
Servy