Bisakah saya melewati parameter dengan referensi di Java?

Jawaban:

225

Java membingungkan karena semuanya dilewatkan oleh nilai . Namun untuk parameter tipe referensi (yaitu bukan parameter tipe primitif) itu adalah referensi itu sendiri yang dilewatkan oleh nilai, maka itu tampaknya menjadi pass-by-reference (dan orang sering mengklaim bahwa itu adalah). Ini tidak terjadi, seperti yang ditunjukkan oleh hal berikut:

Object o = "Hello";
mutate(o)
System.out.println(o);

private void mutate(Object o) { o = "Goodbye"; } //NOT THE SAME o!

Akan mencetak Helloke konsol. Opsi jika Anda ingin mencetak kode di atas Goodbyeadalah dengan menggunakan referensi eksplisit sebagai berikut:

AtomicReference<Object> ref = new AtomicReference<Object>("Hello");
mutate(ref);
System.out.println(ref.get()); //Goodbye!

private void mutate(AtomicReference<Object> ref) { ref.set("Goodbye"); }
oxbow_lakes
sumber
48
Juga, sebuah array dengan panjang 1 dapat digunakan untuk membuat referensi jika Anda benar-benar ingin membingungkan orang :)
Christoffer
2
AtomicReference dan kelas terkait (AtomicInteger) adalah yang terbaik untuk operasi seperti itu
Naveen
3
AtomicReference adalah kerja keras, Anda tidak perlu penghalang memori di sana, dan mereka mungkin dikenakan biaya. JRuby memiliki masalah kinerja karena variabel volatil yang digunakan ketika membangun Obyek. Opsi menggunakan array sebagai referensi ad-hoc tampaknya cukup baik untuk saya, dan saya telah melihatnya menggunakan lebih dari sekali.
Elazar Leibovich
Saya tidak berpikir bahwa ini membingungkan, namun ini adalah perilaku normal. Jenis nilai ada di stack dan tipe referensi ada di heap, tetapi referensi untuk tipe referensi ada di stack juga. Dengan demikian Anda selalu beroperasi dengan nilai yang ada di stack. Saya percaya bahwa pertanyaan di sini adalah: Apakah mungkin untuk melewati referensi dari stack yang menunjuk pada beberapa nilai lain yang juga ada di stack. Atau lulus referensi dari referensi lain yang menunjuk pada beberapa nilai yang hidup di heap?
eomeroff
ini saya info bagus. Saya telah menggunakan array tetapi ini sepertinya sesuatu yang dibangun untuk pekerjaan itu. saya tidak tahu tentang kinerja array vs ini, jika dalam loop kinerja kritis
steveh
65

Bisakah saya melewati parameter dengan referensi di Java?

Tidak.

Mengapa Java hanya memiliki satu mode meneruskan argumen ke metode: dengan nilai.

catatan:

Untuk primitif ini mudah dimengerti: Anda mendapatkan salinan nilainya.

Untuk semua yang lain Anda mendapatkan salinan referensi dan ini disebut juga lewat nilai.

Semuanya ada dalam gambar ini:

masukkan deskripsi gambar di sini

PeterMmm
sumber
25

Di Jawa tidak ada pada tingkat bahasa yang mirip dengan ref . Di Jawa hanya ada yang lewat nilai semantik

Demi rasa ingin tahu Anda dapat menerapkan semantik seperti-ref di Jawa hanya membungkus objek Anda dalam kelas yang bisa berubah:

public class Ref<T> {

    private T value;

    public Ref(T value) {
        this.value = value;
    }

    public T get() {
        return value;
    }

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

Kasus cobaan:

public void changeRef(Ref<String> ref) {
    ref.set("bbb");
}

// ...
Ref<String> ref = new Ref<String>("aaa");
changeRef(ref);
System.out.println(ref); // prints "bbb"
dfa
sumber
Mengapa tidak menggunakan java.lang.ref.Referensi sebagai gantinya?
Hardcoded
java.lang.ref.Reference tidak memiliki metode set, tidak bisa berubah
DFA
mengapa tidak menggunakan AtomicReference(untuk non-primitif)? Atau AtomicSomething(misalnya AtomicBoolean:) untuk primitif?
Arvin
Sayangnya, <T> tidak menerima tipe primitif. Ingin melakukan ini: Ref <int>
jk7
1
@ jk7 Apakah benar-benar cukup penting untuk harus menggunakan intalih- alih Integer, boolbukannya Booleandan seterusnya? Yaitu, kelas wrapper (yang secara otomatis tidak terbuka jika Anda khawatir tentang sintaks) tidak berfungsi?
Paul Stelian
18

Dari James Gosling dalam "Bahasa Pemrograman Java":

"... Hanya ada satu mode passing parameter di Jawa - lulus dengan nilai - dan itu membuat semuanya menjadi sederhana ..."

Duffymo
sumber
2
Pernyataan dewa bahasa harus dinyatakan sebagai jawabannya.
ingyhere
3
@ingyhere:: The pronouncement of the language god should be declared the answerTidak juga. Terlepas dari apa yang dipikirkan atau diyakini oleh pencipta Jawa, jawaban absolut akan datang dari kutipan dari spesifikasi bahasa.
paercebal
1
Sebenarnya itu ada di JLS, di bagian 8.4.1 , dan JLS mengutip Gosling sebagai penulis pertama: "Ketika metode atau konstruktor dipanggil (§15.12), nilai-nilai dari argumen aktual ekspresi menginisialisasi variabel parameter yang baru dibuat , masing-masing dari tipe yang dideklarasikan, sebelum eksekusi tubuh metode atau konstruktor. "
ingyhere
Persis. Sarkasme dari anggota perwakilan rendah tidak membantu.
duffymo
11

Saya pikir kamu tidak bisa. Pilihan terbaik Anda mungkin untuk merangkum hal yang ingin Anda sampaikan "dengan ref" ke instance kelas lain, dan meneruskan referensi kelas (luar) (berdasarkan nilai). Jika Anda melihat apa yang saya maksud ...

yaitu metode Anda mengubah keadaan internal objek yang dilewati, yang kemudian terlihat oleh pemanggil.

Marc Gravell
sumber
7

Java selalu melewati nilai.

Ketika Anda melewati primitif itu adalah salinan nilai, ketika Anda melewati objek itu adalah salinan dari pointer referensi.

Nick Holt
sumber
4

Opsi lain adalah menggunakan array, mis. void method(SomeClass[] v) { v[0] = ...; } Tetapi 1) array harus diinisialisasi sebelum metode dipanggil, 2) masih ada yang tidak dapat mengimplementasikan mis. Metode swap dengan cara ini ... Cara ini digunakan di JDK, misalnya dalam java.util.concurrent.atomic.AtomicMarkableReference.get(boolean[]).

Michal Misiak
sumber