Java是否有类似C#的ref和out关键字的东西?


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


5
海事组织,你不会错过太多。我唯一一次使用C#ref还是out在C#中使用该模式时TryParse(),该方法将返回布尔结果,而从中获取解析值的唯一方法是使用refout
罗伯特·哈维

3
猜猜是什么,那是我需要使用的!;)
吞噬了极乐世界

另一种方法是返回一个既包含状态又包含可空值的复合对象。但是我承认这有点像鲁伯·戈德堡式的。
罗伯特·哈维

1
如果只有一个预定义的可用对象(即元组),则返回复合对象没什么问题。但是,等等,这需要使用原始类型的非擦除泛型才能有效:)
Pavel Minaev 2010年

Answers:


102

不,Java没有像C#这样的东西refout通过引用传递的关键字。

您只能在Java中按值传递。甚至引用也按值传递。有关更多信息,请参见Jon Skeet的页面,有关Java中的参数传递

要执行类似的操作,ref或者out必须将参数包装在另一个对象中,然后将该对象引用作为参数传递。


5
这应该扩大一些。您只能将原语(int,short,char等)作为值传递。不,没有。
科里·桑沃尔德

14
那不是100%正确,如果您传递数组或类,则按值传递对数组或对象的引用,您可以更改数组或对象的内部,它将在调用者中反映出来。
罗曼·希波

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

28
哎哟。真丑
吞噬了极乐世界

46
我从来没有说过,您可以用这种优雅的方式做到这一点:P
OscarRyz,2010年

那么为什么以下操作无效:私有静态无效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});
伊拉德·本达

3
@ user311130您的代码很难阅读,但是您可以创建一个新的问题,例如:“我找到了这个答案<链接到我的答案>,但是以下内容不起作用<您的代码在这里>
OscarRyz 2010年

那太讨厌了,但我仍然给它一个类似的东西,因为它确实以“最干净的”方式解决了问题,这是我现在可以想到的Java时间……
Pangamma

8

据我所知,实际上Java语言既没有ref也没有out等效的关键字。但是,我刚刚将C#代码转换为使用out参数的Java,并会建议我刚刚完成的工作。您应该将任何对象包装到包装器类中,并按以下方法传递包装在包装器对象实例中的值;

使用包装器的简单示例

这是包装类 ;

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

}

真实的例子

使用out参数的AC#.NET方法

这里有一个使用out关键字的C#.NET方法;

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

使用out参数的C#代码的Java等效

包装类的帮助下,此方法的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;
}

包装类

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

}

看到以前的答案。
pashute

给出详细的答案否定投票很有趣。我已经签出了SO,但是找不到带有演示代码的精确答案,这就是为什么我记得的时候才写这个答案的原因。如果您等待一个答案并否决其他答案,那么SO应该禁止所有答案,除非得到问题所有者的确认。
Levent Divilioglu

好的,只有您编辑答案,我才能删除反对票。因此,请阅读其他答案并解释为什么您不想使用这些解决方案。包装器(尤其是REF和OUT包装器只是为了好玩)。5个人提供​​了简短完整的示例答案,只有Eyal基本上写道:“不行”。
pashute

1
这个答案看起来不错。它与最高答案相似,但提供了有关如何在Java中使用包装器类的完整详细示例。
hubatish

8

Java按值传递参数,并且没有任何允许按引用传递的机制。这意味着无论何时传递参数,其值都会被复制到处理调用的堆栈帧中。

我在这里使用的术语“ 价值”需要一些澄清。在Java中,我们有两种变量-基本变量和对象。基元的值是基元本身,而对象的值是其引用(而不是被引用对象的状态)。因此,对方法内部值的任何更改都只会更改堆栈中值的副本,并且调用者将看不到该值。例如,没有任何方法可以实现真正的交换方法,即接收两个引用并交换它们(而不是它们的内容!)。


6

像许多其他人一样,我需要将C#项目转换为Java。我没有在网上找到关于outref修饰符的完整解决方案。但是,我能够获取找到的信息,并对其进行扩展以创建自己的类来满足要求。我想在refout参数之间进行区分以提高代码清晰度。使用以下课程,这是可能的。希望此信息可以节省其他人的时间和精力。

下面的代码中包含一个示例。

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

}

1
这并没有为您提供参数的引用语义,而只是使用反射来使设计为不可变的对象变异,这是一个糟糕的主意,并且仍然没有为参数提供引用语义。
Servy
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.