Есть ли в Java что-то вроде ключевых слов ref и out в C #?

113

Примерно так:

исх пример:

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

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

наш пример:

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

void main() {
    String abc;
    changeString(out abc);
    System.out.println(abc); //prints "def"
}
пожрал элизиум
источник
1
возможный дубликат Могу ли я передавать параметры по ссылке в Java?
Роберт Харви
5
ИМО, вы не так много упускаете. Единственный раз, когда я использую refили outв C #, - это когда я использую такой шаблон, как TryParse(), когда метод возвращает логический результат, и единственный способ получить из него проанализированное значение - использовать refили out.
Роберт Харви
3
Угадайте, что мне нужно использовать! ;)
пожрал элизиум
Другой способ сделать это - вернуть составной объект со статусом и значением, допускающим значение NULL. Но я признаю, что это немного похоже на Руби Голдберга.
Роберт Харви
1
Нет ничего плохого в возврате составного объекта, если бы только был предопределенный пригодный для использования (то есть кортежи). Но подождите, для этого понадобятся нестертые дженерики, работающие с примитивными типами :)
Павел Минаев

Ответы:

102

Нет, в Java нет чего-то вроде C # refи outключевых слов для передачи по ссылке.

В Java можно передавать только по значению. Даже ссылки передаются по значению. См. Страницу Джона Скита о передаче параметров в Java для получения более подробной информации.

Для того, чтобы сделать что - то подобное , refили outвам придется обернуть параметры внутри другого объекта и передать эту ссылку на объект в качестве параметра.

Марк Байерс
источник
5
Это следует расширить на некоторых. В качестве значения можно передавать только примитивы (int, short, char и т. Д.). И нет, выхода нет.
Кори Санволд
14
Это не на 100% верно. Если вы передаете массив или класс, ссылка на массив или объект передается по значению, вы можете изменить внутренние элементы массива или объекта, и это будет отражено в вызывающей стороне.
Romain Hippeau
12
@fearofawhackplanet: Гм, если вы не используете ref.
Роберт Харви
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
Марк Байерс,
5
С точки зрения CLR, вы передаете управляемую ссылку ( T&) по значению, да. Но у C # есть своя собственная терминология, и она конкретно не включает такие вещи, как значения типа ref T- с точки зрения C #, refэто строго модификатор параметра, и говорить о «передаче ссылки по значению» по отношению к нему не имеет смысла. .
Павел Минаев
30

Прямой ответ: нет

Но вы можете смоделировать ссылку с помощью оберток .

И сделайте следующее:

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

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

Вне

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

И практически любой другой тип, например:

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

out.println( one ); // May print 2
OscarRyz
источник
28
Ой. Это уродливо.
пожрал элизиум
46
Я никогда не говорил, что вы можете сделать это таким элегантным способом : P
OscarRyz
так почему следующее не работает: private static void ParseLine (String newline, String [] aWrapper, Integer [] bWrapper) {StringTokenizer st = new StringTokenizer (newline); aWrapper [0] = st.nextToken (); bWrapper [0] = новое целое число (st.nextToken ()); } ParseLine (новая строка, новая строка [] {a}, новое целое число [] {b});
Элад Бенда
3
@ user311130 Ваш код трудно читать, но вы можете создать новый вопрос, например: «Я нашел этот ответ <ссылка на мой ответ>, но следующее не работает <ваш код здесь>
OscarRyz
Это неприятно, но я все еще ставлю лайк, потому что он решает проблему самым «чистым» способом, который я могу придумать для Java на данный момент ...
Пангамма
8

На самом деле, насколько мне известно, в языке Java нет эквивалента ключевого слова ref или out . Однако я только что преобразовал код C # в Java, который использует параметр out и сообщит, что я только что сделал. Вы должны заключить любой объект в класс-оболочку и передать значения, заключенные в экземпляр объекта-оболочки, как показано ниже;

Простой пример использования Wrapper

Вот класс 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;
    }
}

А вот и тестовый код;

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

}

Пример из реального мира

Метод AC # .NET с использованием параметра out

Здесь есть метод C # .NET, который использует ключевое слово 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 кода C #, использующего параметр out

И Java- эквивалент этого метода с помощью класса-оболочки выглядит следующим образом:

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

И класс-оболочка, используемый в этом Java-коде, выглядит следующим образом:

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

}
Левент Дивилиоглу
источник
см. предыдущие ответы.
пашуте
Забавно давать развернутый ответ отрицательный голос. Я проверил SO и не смог найти точного ответа с демонстрационным кодом, поэтому я написал этот ответ, насколько я помню. Если вы ждете одного ответа и голосуете против каждого другого ответа, тогда SO должен запретить все ответы, кроме тех, которые получили подтверждение от владельца вопроса.
Левент Дивилиоглу
Хорошо, я могу снять отрицательный голос, только если вы отредактируете ответ. Так что прочтите другие ответы и объясните, почему вы не хотели использовать эти решения. Обертка (и, в частности, обертка REF и OUT просто для удовольствия). 5 человек дали короткие и полные ответы с примерами , и только Эяль в основном написал: «Нет, не можешь».
пашуте
1
Этот ответ выглядит нормально. Он похож на верхний ответ, но дает полностью подробные примеры того, как использовать класс-оболочку в Java.
Habatish
8

Java передает параметры по значению и не имеет механизма, разрешающего передачу по ссылке. Это означает, что всякий раз, когда параметр передается, его значение копируется в кадр стека, обрабатывающий вызов.

Термин « ценность» в том виде, в котором я его здесь использую, требует небольшого пояснения. В Java есть два типа переменных - примитивы и объекты. Значение примитива - это сам примитив, а значение объекта - его ссылка (а не состояние объекта, на который ссылаются). Следовательно, любое изменение значения внутри метода изменит только копию значения в стеке и не будет замечено вызывающим. Например, нет никакого способа реализовать настоящий метод подкачки, который получает две ссылки и меняет местами их (не их содержимое!).

Эяль Шнайдер
источник
6

Как и многим другим, мне нужно было преобразовать проект C # в Java. Я не нашел в сети полного решения относительно модификаторов out и ref . Но я смог взять информацию, которую я нашел, и расширить ее, чтобы создать свои собственные классы для выполнения требований. Я хотел провести различие между параметрами ref и out для ясности кода. С указанными ниже классами это возможно. Пусть эта информация сэкономит другим время и силы.

Пример включен в код ниже.

//*******************************************************************************************
//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 нет стандартного способа сделать это. Большинство свопов будет выполнено в списке, который упакован в классе. но есть неофициальный способ сделать это:

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

}
Тай Нгуен
источник
2
Это не дает вам ссылочную семантику для параметра, а просто использует отражение для изменения объекта, который разработан как неизменяемый, что является ужасной идеей и по-прежнему не предоставляет ссылочную семантику для параметра.
Servy