Bagaimana melakukan ekivalen lewat referensi untuk primitif di Jawa

116

Kode Java ini:

public class XYZ {   
    public static void main(){  
        int toyNumber = 5;   
        XYZ temp = new XYZ();  
        temp.play(toyNumber);  
        System.out.println("Toy number in main " + toyNumber);  
    }

    void play(int toyNumber){  
        System.out.println("Toy number in play " + toyNumber);   
        toyNumber++;  
        System.out.println("Toy number in play after increement " + toyNumber);   
    }   
}  

akan mengeluarkan ini:

 
Nomor mainan dalam permainan 5  
Nomor mainan dalam permainan setelah peningkatan 6  
Nomor mainan di 5 utama  

Dalam C ++ saya dapat mengirimkan toyNumbervariabel sebagai referensi lewat untuk menghindari bayangan yaitu membuat salinan variabel yang sama seperti di bawah ini:

void main(){  
    int toyNumber = 5;  
    play(toyNumber);  
    cout << "Toy number in main " << toyNumber << endl;  
}

void play(int &toyNumber){  
    cout << "Toy number in play " << toyNumber << endl;   
    toyNumber++;  
    cout << "Toy number in play after increement " << toyNumber << endl;   
} 

dan keluaran C ++ akan menjadi ini:

Nomor mainan dalam permainan 5  
Nomor mainan dalam permainan setelah peningkatan 6  
Nomor mainan di 6 utama  

Pertanyaan saya adalah - Apa kode yang setara di Java untuk mendapatkan output yang sama dengan kode C ++, mengingat bahwa Java adalah pass by value daripada pass by reference ?

Siswa
sumber
22
Ini sepertinya bukan duplikat bagi saya - pertanyaan yang seharusnya duplikat berkaitan dengan cara kerja java dan apa arti istilahnya, yaitu pendidikan, sementara pertanyaan ini menanyakan secara khusus bagaimana cara mendapatkan sesuatu yang menyerupai perilaku referensi, sesuatu yang banyak C / C ++ / D / Ada programmer mungkin bertanya-tanya untuk menyelesaikan pekerjaan praktis, tidak peduli mengapa java semua nilai-demi-nilai.
DarenW
1
@ DarenW Saya setuju sepenuhnya - telah memilih untuk membuka kembali. Oh, dan Anda memiliki reputasi yang cukup untuk melakukan hal yang sama :-)
Duncan Jones
Diskusi seputar primitif agak menyesatkan, karena pertanyaannya berlaku sama untuk nilai referensi.
shmosel
"Dalam C ++ saya dapat meneruskan variabel toyNumber sebagai referensi untuk menghindari membayangi" - ini tidak membayangi karena toyNumbervariabel yang dideklarasikan dalam mainmetode tidak berada dalam ruang lingkup dalam playmetode. Membayangi di C ++ dan Java hanya terjadi jika ada penumpukan cakupan. Lihat en.wikipedia.org/wiki/Variable_shadowing .
Stephen C

Jawaban:

172

Anda punya beberapa pilihan. Salah satu yang paling masuk akal sangat bergantung pada apa yang Anda coba lakukan.

Pilihan 1: jadikan toyNumber variabel anggota publik di kelas

class MyToy {
  public int toyNumber;
}

kemudian berikan referensi ke MyToy ke metode Anda.

void play(MyToy toy){  
    System.out.println("Toy number in play " + toy.toyNumber);   
    toy.toyNumber++;  
    System.out.println("Toy number in play after increement " + toy.toyNumber);   
}

Pilihan 2: kembalikan nilai alih-alih berikan referensi

int play(int toyNumber){  
    System.out.println("Toy number in play " + toyNumber);   
    toyNumber++;  
    System.out.println("Toy number in play after increement " + toyNumber);   
    return toyNumber
}

Pilihan ini akan membutuhkan sedikit perubahan pada callsite di main sehingga terbaca toyNumber = temp.play(toyNumber);,.

Pilihan 3: Jadikan sebagai variabel kelas atau statis

Jika dua fungsi adalah metode pada kelas yang sama atau contoh kelas, Anda dapat mengubah toyNumber menjadi variabel anggota kelas.

Pilihan 4: Buat array elemen tunggal bertipe int dan teruskan itu

Ini dianggap sebagai peretasan, tetapi terkadang digunakan untuk mengembalikan nilai dari pemanggilan kelas sebaris.

void play(int [] toyNumber){  
    System.out.println("Toy number in play " + toyNumber[0]);   
    toyNumber[0]++;  
    System.out.println("Toy number in play after increement " + toyNumber[0]);   
}
laslowh.dll
sumber
2
Penjelasan yang jelas dan sangat bagus untuk memberikan beberapa pilihan tentang cara membuat kode untuk efek yang diinginkan. Sangat membantu untuk sesuatu yang sedang saya kerjakan sekarang! Sungguh gila bahwa pertanyaan ini ditutup.
DarenW
1
Meskipun pilihan Anda 1 melakukan apa yang Anda coba sampaikan, saya sebenarnya harus kembali dan memeriksanya kembali. Pertanyaannya menanyakan apakah Anda bisa lewat sebagai referensi; jadi sebaiknya Anda melakukan printlns dalam lingkup yang sama dengan mainan yang diteruskan ke fungsi tersebut. Cara Anda memilikinya, printlns dioperasikan dalam lingkup yang sama dengan kenaikan yang tidak benar-benar membuktikan maksudnya, TENTU salinan lokal yang dicetak setelah kenaikan akan memiliki nilai yang berbeda, namun itu tidak berarti bahwa referensi yang diteruskan akan memiliki nilai yang berbeda. Sekali lagi, contoh Anda berhasil, tetapi tidak membuktikan maksudnya.
nol298
Tidak, saya pikir itu benar adanya. Setiap fungsi "play ()" dalam jawaban saya di atas dimaksudkan sebagai pengganti drop-in untuk fungsi "play ()" asli, yang juga mencetak nilai sebelum dan sesudah penambahan. Pertanyaan asli memiliki println () di bagian utama yang membuktikan lulus sebagai referensi.
laslowh
Pilihan 2 memerlukan penjelasan lebih lanjut: situs panggilan di utama harus diubah toyNumber = temp.play(toyNumber);agar berfungsi sesuai keinginan.
ToolmakerSteve
1
Ini sangat jelek dan memiliki nuansa hackish ... mengapa sesuatu yang begitu mendasar bukan bagian dari bahasa?
shinzou
30

Java tidak dipanggil dengan referensi melainkan hanya dipanggil berdasarkan nilai

Tetapi semua variabel tipe objek sebenarnya adalah pointer.

Jadi, jika Anda menggunakan Objek yang Dapat Diubah, Anda akan melihat perilaku yang Anda inginkan

public class XYZ {

    public static void main(String[] arg) {
        StringBuilder toyNumber = new StringBuilder("5");
        play(toyNumber);
        System.out.println("Toy number in main " + toyNumber);
    }

    private static void play(StringBuilder toyNumber) {
        System.out.println("Toy number in play " + toyNumber);
        toyNumber.append(" + 1");
        System.out.println("Toy number in play after increement " + toyNumber);
    }
}

Output dari kode ini:

run:
Toy number in play 5
Toy number in play after increement 5 + 1
Toy number in main 5 + 1
BUILD SUCCESSFUL (total time: 0 seconds)

Anda juga dapat melihat perilaku ini di pustaka standar. Misalnya Collections.sort (); Collections.shuffle (); Metode ini tidak mengembalikan daftar baru tetapi mengubah objek argumennya.

    List<Integer> mutableList = new ArrayList<Integer>();

    mutableList.add(1);
    mutableList.add(2);
    mutableList.add(3);
    mutableList.add(4);
    mutableList.add(5);

    System.out.println(mutableList);

    Collections.shuffle(mutableList);

    System.out.println(mutableList);

    Collections.sort(mutableList);

    System.out.println(mutableList);

Output dari kode ini:

run:
[1, 2, 3, 4, 5]
[3, 4, 1, 5, 2]
[1, 2, 3, 4, 5]
BUILD SUCCESSFUL (total time: 0 seconds)
Kerem Baydoğan
sumber
2
Ini bukanlah jawaban atas pertanyaan. Itu akan menjawab pertanyaan, jika disarankan membuat array integer yang berisi satu elemen, dan kemudian memodifikasi elemen itu di dalam metode play; mis mutableList[0] = mutableList[0] + 1;. Seperti yang disarankan Ernest Friedman-Hill.
ToolmakerSteve
Dengan non-primitif. Nilai objek bukan referensi. Mungkin Anda ingin mengatakan: "nilai parameter" adalah "referensi". Referensi diteruskan oleh nilai.
vlakov
Saya mencoba melakukan sesuatu yang serupa tetapi dalam kode Anda, metode private static void play(StringBuilder toyNumber)- bagaimana Anda menyebutnya jika misalnya public static int yang mengembalikan integer? Karena saya memiliki metode yang mengembalikan angka tetapi jika saya tidak memanggilnya di suatu tempat, itu tidak digunakan.
frank17
18

Membuat

class PassMeByRef { public int theValue; }

kemudian berikan referensi ke salah satu instance darinya. Perhatikan bahwa metode yang mengubah status melalui argumennya sebaiknya dihindari, terutama dalam kode paralel.

Ingo
sumber
3
Tidak disukai oleh saya. Ini salah dalam banyak hal. Java adalah nilai yang dilewatkan - selalu. Tidak ada pengecualian.
duffymo
14
@ duffymo - Tentu saja Anda dapat memberikan suara negatif sesuka Anda - tetapi apakah Anda sudah mempertimbangkan apa yang diminta OP? Dia ingin lulus dan int dengan referensi - dan hanya ini dicapai jika saya melewatkan nilai referensi ke contoh di atas.
Ingo
7
@ Duffymo: Hah? Anda salah, atau salah paham dengan pertanyaannya. Ini adalah salah satu cara tradisional di Jawa untuk melakukan apa yang diminta OP.
ToolmakerSteve
5
@ duffymo: Komentar Anda salah pada banyak tingkatan. SO adalah tentang memberikan jawaban dan komentar yang berguna, dan Anda hanya memberikan suara negatif pada jawaban yang benar hanya karena (saya kira) itu tidak sesuai dengan gaya dan filosofi pemrograman Anda. Bisakah Anda setidaknya menawarkan penjelasan yang lebih baik, atau bahkan jawaban yang lebih baik untuk pertanyaan awal?
paercebal
2
@duffymo itulah yang saya katakan: lulus ref dengan nilai. Menurut Anda bagaimana menurut Anda ref dilakukan dalam bahasa yang memperlambatnya?
Ingo
11

Anda tidak bisa melewatkan primitif dengan referensi di Jawa. Semua variabel tipe objek sebenarnya adalah pointer, tentu saja, tapi kami menyebutnya "referensi", dan mereka juga selalu diteruskan oleh nilai.

Dalam situasi di mana Anda benar-benar perlu meneruskan primitif dengan referensi, yang terkadang dilakukan orang adalah mendeklarasikan parameter sebagai array tipe primitif, dan kemudian meneruskan array elemen tunggal sebagai argumen. Jadi, Anda meneruskan referensi int [1], dan dalam metode ini, Anda dapat mengubah konten array.

Ernest Friedman-Hill
sumber
1
Tidak - semua kelas pembungkus tidak dapat diubah - mereka mewakili nilai tetap yang tidak dapat diubah setelah objek dibuat.
Ernest Friedman-Hill
1
"Anda tidak dapat melewatkan primitif dengan referensi di Java": OP tampaknya memahami ini, karena mereka membedakan C ++ (di mana Anda bisa) dengan Java.
Raedwald
Perlu dicatat bahwa "semua kelas pembungkus tidak dapat diubah" yang dikatakan oleh Ernest berlaku untuk JDK built-in Integer, Double, Long, dll. Anda dapat melewati batasan ini dengan kelas pembungkus kustom Anda, seperti yang dilakukan Jawaban Ingo.
pengguna3207158
10

Untuk solusi cepat, Anda dapat menggunakan AtomicInteger atau variabel atom lainnya yang memungkinkan Anda mengubah nilai di dalam metode menggunakan metode bawaan. Berikut adalah contoh kode:

import java.util.concurrent.atomic.AtomicInteger;


public class PrimitivePassByReferenceSample {

    /**
     * @param args
     */
    public static void main(String[] args) {

        AtomicInteger myNumber = new AtomicInteger(0);
        System.out.println("MyNumber before method Call:" + myNumber.get());
        PrimitivePassByReferenceSample temp = new PrimitivePassByReferenceSample() ;
        temp.changeMyNumber(myNumber);
        System.out.println("MyNumber After method Call:" + myNumber.get());


    }

     void changeMyNumber(AtomicInteger myNumber) {
        myNumber.getAndSet(100);

    }

}

Keluaran:

MyNumber before method Call:0

MyNumber After method Call:100
premSiva
sumber
Saya menggunakan dan menyukai solusi ini karena menawarkan tindakan gabungan yang bagus dan lebih mudah diintegrasikan ke dalam program bersamaan yang sudah atau mungkin ingin menggunakan kelas atom ini di masa mendatang.
RAnders00
2
public static void main(String[] args) {
    int[] toyNumber = new int[] {5};
    NewClass temp = new NewClass();
    temp.play(toyNumber);
    System.out.println("Toy number in main " + toyNumber[0]);
}

void play(int[] toyNumber){
    System.out.println("Toy number in play " + toyNumber[0]);
    toyNumber[0]++;
    System.out.println("Toy number in play after increement " + toyNumber[0]);
}
itun
sumber