Apakah Java memiliki sesuatu seperti kata kunci ref dan out C #?

113

Sesuatu seperti berikut ini:

contoh ref:

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

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

keluar contoh:

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

void main() {
    String abc;
    changeString(out abc);
    System.out.println(abc); //prints "def"
}
melahap elysium
sumber
5
IMO Anda tidak kehilangan banyak. Satu-satunya saat saya menggunakan refatau outdi C # adalah ketika saya menggunakan pola seperti TryParse(), di mana metode mengembalikan hasil boolean, dan satu-satunya cara untuk mendapatkan nilai parsing darinya adalah dengan menggunakan refatau out.
Robert Harvey
3
Tebak, itulah yang benar yang perlu saya gunakan! ;)
melahap elysium
Cara lain untuk melakukannya adalah dengan mengembalikan objek komposit dengan status dan nilai nullable di dalamnya. Tapi saya akui itu agak Rube Goldberg-ish.
Robert Harvey
1
Tidak ada yang salah dengan mengembalikan objek komposit, jika hanya akan ada objek yang dapat digunakan yang telah ditentukan sebelumnya (yaitu tupel). Tapi tunggu, itu membutuhkan obat generik tak terhapus yang bekerja dengan tipe primitif agar efisien :)
Pavel Minaev

Jawaban:

102

Tidak, Java tidak memiliki sesuatu seperti C # refdan outkata kunci untuk lewat referensi.

Anda hanya dapat memberikan nilai di Java. Bahkan referensi dilewatkan oleh nilai. Lihat halaman Jon Skeet tentang penerusan parameter di Java untuk lebih jelasnya.

Untuk melakukan sesuatu yang serupa refatau outAnda harus membungkus parameter Anda di dalam objek lain dan meneruskan referensi objek itu sebagai parameter.

Mark Byers
sumber
5
Ini harus diperluas pada beberapa. Anda hanya dapat memberikan primitif (int, short, char, dll.) Sebagai nilai. Dan tidak, tidak ada jalan keluar.
Corey Sunwold
14
Itu tidak 100% benar, Jika Anda meneruskan dalam sebuah larik atau kelas, referensi ke larik atau objek diteruskan dengan nilai, Anda dapat mengubah internal ke larik atau objek dan itu akan tercermin di pemanggil.
Romain Hippeau
12
@fearofawhackplanet: Um, kecuali Anda menggunakan 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
Dari perspektif CLR, Anda meneruskan referensi terkelola ( T&) berdasarkan nilai, ya. Tetapi C # memiliki terminologi sendiri, dan secara khusus tidak menyertakan hal-hal seperti nilai-nilai tipe ref T- dari perspektif C #, refsecara ketat adalah pengubah parameter, dan berbicara tentang "meneruskan referensi berdasarkan nilai" sehubungan dengan itu tidak masuk akal .
Pavel Minaev
30

Jawaban langsung: Tidak

Tapi Anda bisa mensimulasikan referensi dengan pembungkus .

Dan lakukan hal berikut:

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

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

Di luar

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

Dan pada dasarnya semua jenis lainnya seperti:

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

out.println( one ); // May print 2
OscarRyz
sumber
28
Aduh. Jelek sekali.
melahap elysium
46
Saya tidak pernah berkata, Anda dapat melakukannya dengan cara yang elegan ini : P
OscarRyz
jadi mengapa hal berikut tidak berhasil: private static void ParseLine (String newline, String [] aWrapper, Integer [] bWrapper) {StringTokenizer st = new StringTokenizer (newline); aWrapper [0] = st.nextToken (); bWrapper [0] = bilangan bulat baru (st.nextToken ()); } ParseLine (baris baru, String baru [] {a}, Integer baru [] {b});
Elad Benda
3
@ user311130 Kode Anda sulit dibaca, tetapi Anda dapat membuat pertanyaan baru yang mengatakan sesuatu seperti: "Saya menemukan jawaban ini <tautan ke jawaban saya> tetapi yang berikut ini tidak berfungsi <kode Anda di sini>
OscarRyz
Itu menjijikkan, tapi saya tetap memberikan ini sejenisnya karena itu memecahkan masalah dengan cara "terbersih" yang dapat saya pikirkan untuk Java saat ini ...
Pangamma
8

Sebenarnya tidak ada ref atau out kata kunci yang setara dalam bahasa Jawa sejauh yang saya tahu. Namun saya baru saja mengubah sebuah C # kode ke Java yang menggunakan keluar parameter dan akan memberitahu apa yang baru saja dilakukan. Anda harus membungkus objek apa pun ke dalam kelas pembungkus dan meneruskan nilai yang dibungkus dalam contoh objek pembungkus sebagai berikut;

Contoh Sederhana Untuk Menggunakan Wrapper

Ini adalah Kelas 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;
    }
}

Dan inilah kode tesnya;

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

}

Contoh Dunia Nyata

Metode AC # .NET menggunakan parameter keluar

Berikut ada C # .NET metode yang menggunakan keluar kata kunci;

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

Setara Java dari kode C # yang menggunakan parameter keluar

Dan persamaan Java untuk metode ini dengan bantuan kelas pembungkus adalah sebagai berikut;

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

Kelas Pembungkus

Dan kelas pembungkus yang digunakan dalam kode Java ini adalah sebagai berikut;

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
sumber
lihat jawaban sebelumnya.
pashute
Memberikan jawaban rinci dengan suara negatif itu lucu. Saya telah memeriksa SO dan tidak dapat menemukan jawaban pasti yang bagus dengan kode demo, itulah mengapa saya menulis jawaban ini sejauh yang saya ingat. Jika Anda menunggu satu jawaban dan menolak setiap jawaban lainnya, maka SO harus melarang semua jawaban selain yang mendapat konfirmasi dari pemilik pertanyaan.
Levent Divilioglu
Oke, saya hanya dapat menghapus suara negatif jika Anda mengedit jawabannya. Jadi baca jawaban lainnya dan jelaskan mengapa Anda tidak ingin menggunakan solusi tersebut. Pembungkus (dan khususnya pembungkus REF dan OUT hanya untuk kesenangan). 5 orang memberikan jawaban singkat dan lengkap dengan contoh , dan hanya Eyal yang pada dasarnya menulis: "Tidak, Anda tidak bisa".
pashute
1
Jawaban ini terlihat bagus. Ini mirip dengan jawaban teratas, tetapi memberikan contoh yang sangat mendetail tentang cara menggunakan kelas pembungkus di Java.
hubatish
8

Java meneruskan parameter berdasarkan nilai dan tidak memiliki mekanisme apa pun untuk mengizinkan referensi lewat. Itu berarti bahwa setiap kali parameter dilewatkan, nilainya disalin ke frame stack yang menangani panggilan.

Istilah nilai yang saya gunakan di sini membutuhkan sedikit klarifikasi. Di Jawa kami memiliki dua jenis variabel - primitif dan objek. Nilai primitif adalah primitif itu sendiri, dan nilai suatu objek adalah referensinya (dan bukan status objek yang direferensikan). Oleh karena itu, setiap perubahan pada nilai di dalam metode hanya akan mengubah salinan nilai dalam tumpukan, dan tidak akan terlihat oleh pemanggil. Misalnya, tidak ada cara untuk mengimplementasikan metode swap nyata, yang menerima dua referensi dan menukar mereka (bukan isinya!).

Eyal Schneider
sumber
6

Seperti banyak orang lainnya, saya perlu mengonversi proyek C # ke Java. Saya tidak menemukan solusi lengkap di web mengenai pengubah referensi dan keluar . Tetapi, saya dapat mengambil informasi yang saya temukan, dan mengembangkannya untuk membuat kelas saya sendiri untuk memenuhi persyaratan. Saya ingin membuat perbedaan antara parameter ref dan out untuk kejelasan kode. Dengan kelas di bawah ini, itu mungkin. Semoga informasi ini menghemat waktu dan tenaga orang lain.

Contoh disertakan dalam kode di bawah ini.

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

java tidak memiliki cara standar untuk melakukannya. Kebanyakan swap akan dilakukan pada daftar yang dikemas di kelas. tetapi ada cara tidak resmi untuk melakukannya:

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
sumber
1
Itu tidak memberi Anda semantik referensi untuk parameter, itu hanya menggunakan refleksi untuk mengubah objek yang dirancang untuk menjadi tetap, yang merupakan ide yang buruk , dan masih tidak memberikan semantik referensi untuk parameter.
Pelayanan