Java có một cái gì đó giống như các từ khóa ref và out của C #?


113

Một cái gì đó như sau:

ví dụ ref:

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

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

ra ví dụ:

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

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


5
IMO bạn không thiếu nhiều. Lần duy nhất tôi từng sử dụng refhoặc outtrong C # là khi tôi đang sử dụng một mẫu như TryParse(), trong đó phương thức trả về một kết quả boolean và cách duy nhất để lấy giá trị được phân tích cú pháp từ nó là sử dụng refhoặc out.
Robert Harvey

3
Đoán xem, đó là đúng những gì tôi cần sử dụng! ;)
nuốt chửng elysium

Một cách khác để làm điều đó là trả về một đối tượng hỗn hợp có cả trạng thái và giá trị nullable trong đó. Nhưng tôi thừa nhận đó là một chút Rube Goldberg-ish.
Robert Harvey

1
Không có gì sai khi trả về một đối tượng tổng hợp, nếu chỉ có một đối tượng có thể sử dụng được xác định trước (tức là các bộ giá trị). Nhưng chờ đã, điều đó sẽ cần các generic không bị xóa làm việc với các loại nguyên thủy để có hiệu quả :)
Pavel Minaev

Câu trả lời:


102

Không, Java không có thứ gì đó giống như C # refoutcác từ khóa để chuyển qua tham chiếu.

Bạn chỉ có thể chuyển theo giá trị trong Java. Ngay cả các tham chiếu cũng được truyền theo giá trị. Xem trang của Jon Skeet về truyền tham số trong Java để biết thêm chi tiết.

Để làm điều gì đó tương tự refhoặc outbạn sẽ phải bọc các tham số của mình bên trong một đối tượng khác và chuyển tham chiếu đối tượng đó vào dưới dạng một tham số.


5
Điều này nên được mở rộng trên một số. Bạn chỉ có thể chuyển các nguyên thủy (int, short, char, v.v.) làm giá trị. Và không, không có.
Corey Sunwold

14
Điều đó không đúng 100%, Nếu bạn truyền vào một mảng hoặc một lớp thì tham chiếu đến mảng hoặc đối tượng được chuyển vào theo giá trị, Bạn có thể thay đổi nội bộ thành mảng hoặc đối tượng và nó sẽ được phản ánh trong trình gọi.
Romain Hippeau

12
@fearofawhackplanet: Ừm, trừ khi bạn sử dụng 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
Từ quan điểm CLR, bạn đang chuyển một tham chiếu được quản lý ( T&) theo giá trị, vâng. Nhưng C # có thuật ngữ riêng và nó đặc biệt không bao gồm những thứ như giá trị của kiểu ref T- từ góc độ C #, refhoàn toàn là một công cụ sửa đổi tham số và nói về "truyền tham chiếu theo giá trị" đối với nó không có ý nghĩa gì .
Pavel Minaev

30

Câu trả lời trực tiếp: Không

Nhưng bạn có thể mô phỏng tham chiếu với trình bao bọc .

Và làm như sau:

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

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

Ngoài

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

Và về cơ bản bất kỳ loại nào khác như:

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

out.println( one ); // May print 2

28
Ôi chao. Thật là xấu xí.
elysium nuốt chửng

46
Tôi chưa bao giờ nói, Bạn có thể làm điều đó theo cách thanh lịch này : P
OscarRyz

vậy tại sao phần sau không hoạt động: 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,

3
@ user311130 Mã của bạn là khó đọc, nhưng bạn có thể tạo ra một câu hỏi mới nói cái gì đó như: "Tôi tìm thấy câu trả lời này <link vào câu trả lời của tôi> nhưng sau không làm việc <code của bạn ở đây>
OscarRyz

Điều đó thật khó chịu, nhưng tôi vẫn cho điều này một lượt thích vì nó giải quyết vấn đề theo cách "sạch sẽ nhất" mà tôi có thể nghĩ đến cho Java tại thời điểm này ...
Pangamma 23/1218

8

Trên thực tế, không có từ khóa refout nào tương đương trong ngôn ngữ Java theo như tôi biết. Tuy nhiên tôi đã chỉ cần chuyển một C # mã vào Java mà sử dụng ra tham số và sẽ tư vấn những gì tôi vừa thực hiện. Bạn nên bọc bất kỳ đối tượng nào vào một lớp trình bao bọc và chuyển các giá trị được bao bọc trong thể hiện đối tượng trình bao bọc như sau;

Một ví dụ đơn giản để sử dụng Wrapper

Đây là Lớp 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;
    }
}

Và đây là mã kiểm tra;

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

}

Một ví dụ về thế giới thực

Phương thức AC # .NET sử dụng tham số out

Đây là một phương thức C # .NET đang sử dụng từ khóa 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 Tương đương với mã C # đang sử dụng tham số out

Và tương đương với Java của phương thức này với sự trợ giúp của lớp wrapper như sau;

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

Lớp bọc

lớp trình bao bọc được sử dụng trong mã Java này như bên dưới;

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

}

xem câu trả lời trước đây.
pashute

Đưa ra một câu trả lời chi tiết một cuộc bỏ phiếu tiêu cực là buồn cười. Tôi đã kiểm tra SO và không thể tìm thấy câu trả lời chính xác tốt với mã demo, đó là lý do tại sao tôi đã viết câu trả lời này theo những gì tôi có thể nhớ. Nếu bạn đợi một câu trả lời duy nhất và bỏ phiếu cho mọi câu trả lời khác, thì SO nên cấm tất cả các câu trả lời khác với câu trả lời đã được chủ sở hữu câu hỏi xác nhận.
Levent Divilioglu

Được, tôi chỉ có thể xóa bỏ phiếu phủ định nếu bạn chỉnh sửa câu trả lời. Vì vậy, hãy đọc các câu trả lời khác và giải thích lý do tại sao bạn không muốn sử dụng các giải pháp đó. Một trình bao bọc (và cụ thể là một trình bao bọc REF và OUT chỉ để giải trí). 5 người đã đưa ra câu trả lời ngắn gọn và đầy đủ với các ví dụ , và chỉ có Eyal về cơ bản viết: "Không, bạn không thể".
pashute 19/09/17

1
Câu trả lời này có vẻ ổn. Nó tương tự như câu trả lời trên cùng, nhưng đưa ra các ví dụ đầy đủ chi tiết về cách sử dụng một lớp trình bao bọc trong Java.
hubatish

8

Java chuyển các tham số theo giá trị và không có bất kỳ cơ chế nào để cho phép chuyển theo tham chiếu. Điều đó có nghĩa là bất cứ khi nào một tham số được truyền, giá trị của nó được sao chép vào khung ngăn xếp xử lý lệnh gọi.

Giá trị thuật ngữ mà tôi sử dụng ở đây cần được làm rõ một chút. Trong Java, chúng ta có hai loại biến - nguyên thủy và đối tượng. Giá trị của một nguyên thủy là chính nguyên thủy và giá trị của một đối tượng là tham chiếu của nó (chứ không phải trạng thái của đối tượng được tham chiếu). Do đó, bất kỳ thay đổi nào đối với giá trị bên trong phương thức sẽ chỉ thay đổi bản sao của giá trị trong ngăn xếp và người gọi sẽ không nhìn thấy. Ví dụ: không có bất kỳ cách nào để triển khai phương thức hoán đổi thực, phương thức này nhận hai tham chiếu và hoán đổi chúng (không phải nội dung của chúng!).


6

Giống như nhiều người khác, tôi cần chuyển đổi một dự án C # sang Java. Tôi đã không tìm thấy một giải pháp hoàn chỉnh trên web về công cụ sửa đổi outref . Nhưng, tôi có thể lấy thông tin tôi tìm thấy và mở rộng thông tin đó để tạo các lớp của riêng tôi để đáp ứng các yêu cầu. Tôi muốn phân biệt giữa các tham số refout để làm rõ mã. Với các lớp dưới đây, điều đó là hoàn toàn có thể. Mong thông tin này tiết kiệm thời gian và công sức cho người khác.

Một ví dụ được bao gồm trong đoạn mã dưới đây.

//*******************************************************************************************
//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 không có cách làm tiêu chuẩn. Hầu hết các hoán đổi sẽ được thực hiện trên danh sách được đóng gói trong lớp. nhưng có một cách không chính thức để làm điều đó:

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
Điều đó không cung cấp cho bạn ngữ nghĩa tham chiếu cho một tham số, đó chỉ là sử dụng phản xạ để biến đổi một đối tượng được thiết kế để không thay đổi, đó là một ý tưởng tồi tệ và vẫn không cung cấp ngữ nghĩa tham chiếu cho tham số.
Servy
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.