Meneruskan 'ini' dalam panggilan metode yang diterima praktik di java

93

Apakah praktik yang baik / buruk / dapat diterima untuk meneruskan objek saat ini dalam pemanggilan metode. Seperti dalam:

public class Bar{
    public Bar(){}

    public void foo(Baz baz){
        //  modify some values of baz
    }
}

public class Baz{
    //constructor omitted

    public void method(){
        Bar bar = new Bar();
        bar.foo(this);
    }
}

Secara khusus, apakah saluran tersebut bar.foo(this)dapat diterima?

maxf130
sumber
60
Mengapa itu tidak bisa diterima? Itu biasa.
Denys Séguret
3
Jadi ... itu 8 ya :) (Dan ya, saya menjaganya agar tetap diperbarui.)
Alex Gittemeier
2
kelas anonim non-statis otomatis meneruskan referensi kelas super ini, jadi ini dapat diterima. satu-satunya kehati-hatian adalah berhati-hati dengan referensi melingkar.
Mehul Rathod
5
Namun ada satu peringatan: Anda tidak boleh meneruskan ini dalam konstruktor karena itu akan mengekspos objek Anda dalam keadaan tidak konsisten. Orang biasanya melakukan itu ketika mereka membuat callback (misalnya ActionListener) sebagai kelas dalam anonim dan kemudian meneruskannya ke objek lain.
Tamas Rev
3
@dystroy meskipun saya setuju itu dapat diterima, menyiratkan bahwa itu dapat diterima karena itu umum adalah logika yang sangat buruk. Melakukan sesuatu karena hal biasa dapat membuat Anda mendapat banyak masalah
Carrie Kendall

Jawaban:

155

Tidak ada alasan untuk tidak menggunakannya, thisini adalah contoh saat ini dan sangat sah untuk digunakan. Nyatanya seringkali tidak ada cara yang bersih untuk menghilangkannya.

Jadi gunakan itu.

Karena sulit untuk meyakinkan itu dapat diterima tanpa contoh (jawaban negatif untuk pertanyaan seperti itu selalu lebih mudah untuk diperdebatkan), saya baru saja membuka salah satu kelas yang paling umum java.lang, yang Stringsatu, dan tentu saja saya menemukan contoh penggunaan ini, misalnya

1084        // Argument is a String
1085        if (cs.equals(this))
1086            return true;

Mencari (this dalam proyek-proyek besar yang "diterima", Anda tidak akan gagal menemukannya.

Denys Séguret
sumber
6
Jawaban yang sempurna.
maxf130
15
-1 karena tidak disebutkan fakta bahwa hubungan kelas dua arah lebih rumit daripada hubungan satu arah. Sangat penting untuk memastikan bahwa perangkat lunak sejelas mungkin. Dalam contoh spesifik di atas, akan lebih masuk akal untuk Memindahkan Metode ke kelas Baz untuk menghindari referensi dua arah antara dua kelas dan untuk menyatukan perilaku dan data.
JW.
35
-1 untuk jawaban karena Anda menemukan dalam pertanyaan sebuah detail kecil tambahan yang dapat Anda komentari? Betulkah ?
Denys Séguret
13
@dystroy Ya, saya tidak setuju dengan frasa pembukaan Anda: "Tidak ada alasan untuk tidak menggunakannya".
JW.
4
Dalam praktiknya, dalam kode nyata (berlawanan dengan contoh OP yang disederhanakan), lewat thistidak berarti Anda menambahkan tautan dua arah, karena pewarisan dan antarmuka misalnya.
Denys Séguret
165

Tidak ada yang salah dengan itu. Apa yang BUKAN praktik yang baik adalah melakukan hal yang sama di dalam konstruktor, karena Anda akan memberikan referensi ke objek yang belum sepenuhnya diinisialisasi.

Ada semacam posting serupa di sini: Java membocorkan ini di konstruktor di mana mereka memberikan penjelasan mengapa yang terakhir adalah praktik yang buruk.

morgano
sumber
18
+1: bagus untuk menunjukkan bahaya dalam mengacu pada 'ini' di konstruktor.
Batsyeba
Ini belum tentu merupakan praktik yang buruk. Misalnya, Carkonstruktor dapat membuat Wheelinstance, Cartanpa yang Wheeldiinisialisasi tidak lengkap, sedangkan Wheeltanpa korespondensi Carjuga akan tidak diinisialisasi secara lengkap. Dalam hal ini, mungkin dapat diterima di konstruktor Mobil untuk meneruskan thiske konstruktor Roda. Alternatif lain adalah membuat Mobil dan Roda memiliki konstruktor pribadi dan menggunakan fungsi pabrik yang membangun Mobil, Roda, dan memasang Roda pada Mobil; tetapi apakah itu harus menjadi metode statis di Mobil atau metode statis di Wheel?
Lie Ryan
Jelas, Anda harus membuat CarFactoryWheelInstallerProxyyang menginstal roda untuk Anda.
Kevin
6
@LieRyan Wheelsepenuhnya di bawah Car, dan IMO seharusnya tidak tahu Carsama sekali.
Izkata
3
Satu-satunya hal buruk tentang menggunakan thisdari dalam konstruktor adalah jika thisditeruskan ke metode atau konteks dari mana referensi objek yang belum sepenuhnya dibuat dipublikasikan ke klien yang tidak tepercaya atau tidak dikenal (atau kode klien yang mengasumsikan memiliki pandangan ke objek yang sepenuhnya dibangun). Meneruskan thisdari konstruktor ke metode paket-privat yang melakukan inisialisasi umum, menurut pendapat saya, tidak hanya dapat diterima tetapi juga diinginkan.
scottb
42

Ya , tetapi Anda harus berhati-hati tentang dua hal

  1. Melewati ini ketika objek belum dibangun (yaitu dalam konstruktornya)
  2. Meneruskan ini ke objek berumur panjang, yang akan membuat referensi tetap hidup dan akan mencegah objek ini dari pengumpulan sampah.
Stefanos T.
sumber
1
Perhatikan bahwa konstruktor di Java sebenarnya bukan konstruktor, mungkin lebih tepat untuk memanggil konstruktor Java "penginisialisasi". Dalam konstruktor Java, objek sebenarnya telah dialokasikan memori, ini objek sebenarnya sudah ada / dibangun di dalam konstruktor.
Lie Ryan
1
Tidak juga, objek mungkin memiliki beberapa variabel instan yang belum diinisialisasi, sehingga objek tersebut belum sepenuhnya beroperasi. Jadi, dalam konstruktor, Anda bisa meneruskan ini ke objek kedua, yang bisa memanggil metode ke objek yang belum menginisialisasi semua variabel instansinya.
Stefanos T.
namun, selama objek kedua menyadari bahwa objek yang diteruskan tidak diinisialisasi, dan memperlakukannya sebagai objek buram, atau hanya memanggil metode yang telah dinyatakan aman dalam keadaan tersebut, tidak akan ada masalah untuk meneruskannya this. Melakukan itu tidak mungkin dilakukan jika objek belum dialokasikan.
Lie Ryan
13

Itu sangat normal dan bisa diterima.

Batsyeba
sumber
5

ini singkatan dari objek saat ini. Apa yang Anda lakukan secara sistematis benar tetapi saya tidak melihat kebutuhan ini jika Anda memanggil metode di kelas yang sama.

Juned Ahsan
sumber
2
Dalam kode contoh, Baz.method () adalah metode instance yang memanggil Bar.foo () dengan instance Baz sebagai parameter. Jadi OP tidak memanggil metode di kelas yang sama.
pengguna
@ MichaelKjörling Saya pikir Juned mengatakan bahwa, dengan memindahkan metode foo () ke kelas Baz, tidak perlu melewati thisantara dua kelas. Jadi, tidak perlu menambah kerumitan ekstra.
JW.
4

Itu adalah praktik yang buruk untuk meneruskan objek saat ini dalam pemanggilan metode jika ada alternatif yang kurang rumit untuk mencapai perilaku yang sama.

Menurut definisi, asosiasi dua arah dibuat segera this diteruskan dari satu objek ke objek lainnya.

Mengutip Refactoring, oleh Martin Fowler:

Ubah Asosiasi Dua Arah menjadi Searah (200)

Asosiasi dua arah berguna, tetapi memiliki harga. Harga adalah kerumitan tambahan dari pemeliharaan tautan dua arah dan memastikan bahwa objek dibuat dan dihapus dengan benar. Asosiasi dua arah tidak alami bagi banyak pemrogram, sehingga sering kali menjadi sumber kesalahan

...

Anda harus menggunakan asosiasi dua arah saat diperlukan, tetapi tidak jika tidak. Segera setelah Anda melihat asosiasi dua arah tidak lagi menarik bobotnya, jatuhkan ujung yang tidak perlu.

Jadi, secara teoritis, kita harus mendengar lonceng peringatan ketika kita merasa perlu untuk lewat this dan berusaha keras untuk memikirkan cara lain untuk menyelesaikan masalah yang ada. Tentu saja, ada kalanya, pada akhirnya, tindakan itu masuk akal.

Juga sering kali perlu merusak desain Anda untuk sementara, melakukan 'praktik buruk', selama refactoring kode jangka panjang Anda untuk perbaikan keseluruhan. (Satu langkah mundur, dua langkah maju).

Dalam praktiknya, saya menemukan kode saya telah meningkat secara besar-besaran dengan menghindari tautan dua arah seperti wabah.

JW.
sumber
Anda mengacaukan contoh sederhana dengan kebutuhan untuk membuat tautan dua arah. Meneruskan ini sebagai parameter, seperti yang seharusnya jelas dengan banyak contoh kode sumber java.lang (misalnya yang Anda lihat di jawaban saya) tidak berarti Anda menambahkan ketergantungan dua arah. Jawaban ini seharusnya menjadi komentar menurut saya.
Denys Séguret
@dystroy Terima kasih telah menambahkan komentar untuk menjelaskan mengapa Anda menurunkan suara. Selalu menyenangkan untuk mengetahuinya. Saya akan mengubah jawaban saya untuk mengklarifikasi bahwa, menurut definisi, asosiasi dua arah dibuat segera setelah thisdisahkan.
JW.
1
"menurut definisi, asosiasi dua arah dibuat segera setelah ini disahkan" . Ini menjelaskan di mana Anda gagal untuk mengerti. Lihat contoh yang saya berikan. Tidak ada tautan dua arah karena tipe argumen di dalam equalsadalah Objek. Ini sangat umum: metode penerima mendefinisikan argumennya sebagai kelas yang lebih umum atau sebagai antarmuka. Salah satu alasan pola seperti itu digunakan di java adalah untuk menghindari ketergantungan yang tidak diinginkan. Sebelum melangkah lebih jauh, saya sarankan Anda melihat banyak kejadian lewat thissebagai argumen di perpustakaan java terhormat.
Denys Séguret
Mari kita setuju untuk tidak setuju. Hidup saya menjadi jauh lebih mudah karena saya menghindari meneruskan thiskode saya, jika memungkinkan. Saya akan merekomendasikan orang lain untuk melakukannya.
JW.
2
@JW: alasan yang diberikan dalam jawaban ini tidak relevan. Selalu merupakan ide yang buruk untuk melakukan X jika ada hal lain yang lebih sederhana, untuk nilai X berapa pun.
Lie Ryan
4

Iya. Anda dapat menggunakannya.Ini hanya umum dalam pemrograman untuk thislulus.Tetapi ada pro dan kontra tentang penggunaan itu.Meski begitu tidak berbahaya untuk melakukannya.

Suresh Atta
sumber
Ada banyak efek samping. Itu menambah kompleksitas.
JW.
Jika banyak efek samping itu ada, kami tidak dapat menemukan bukti tunggal seperti itu di kode sumber java kami. Lihat contoh @destroys dari kode sumber.
Suresh Atta
2

Hanya untuk menambahkan satu contoh lagi dimana passing thisbenar dan mengikuti desain yang baik: Pola pengunjung . Dalam pola desain Pengunjung, metode accept(Visitor v)biasanya diimplementasikan dengan cara yang hanya dipanggil v.visit(this).

Petr Zelenka
sumber
1

Dapat diterima

Cuplikan dari dokumen Oracle JAVA:

Di dalam metode instance atau konstruktor, ini adalah referensi ke objek saat ini - objek yang metode atau konstruktornya dipanggil. Anda dapat merujuk ke anggota mana pun dari objek saat ini dari dalam metode instance atau konstruktor dengan menggunakan ini.

Menggunakan ini dengan Field

Alasan paling umum untuk menggunakan kata kunci ini adalah karena bidang dibayangi oleh metode atau parameter konstruktor.

Nargis
sumber
2
"Anda dapat merujuk ke anggota mana pun dari objek saat ini " - yang tampaknya tidak menjawab pertanyaan "apakah lulus thissebagai parameter dapat diterima ? ".
pengguna
2
Ini mengatakan bagaimana Anda dapat melakukan this.some_variableuntuk merujuk ke variabel kelas daripada ke variabel lokal. Ini tidak ada hubungannya dengan melewatkan thissebagai parameter.
Jose Salvatierra
0

Segala sesuatu di java diteruskan oleh nilai. Tapi objek TIDAK PERNAH diteruskan ke metode!
Ketika java mengoper objek ke metode, pertama-tama java membuat salinan referensi ke objek tersebut, bukan salinan objek itu sendiri. Oleh karena itu, metode ini sangat cocok digunakan di java. Dan penggunaan yang paling sering diikuti.

blganesh101
sumber
9
Ini terlihat dari topik.
Denys Séguret
4
"Segala sesuatu di java dilewatkan oleh nilai." - itu adalah komentar awal yang sangat menyesatkan. Sebenarnya semua objek diteruskan oleh referensi dan semua tipe primitif dilewatkan oleh nilai. Anda TIDAK PERNAH memiliki thisreferensi ke tipe primitif, dan oleh karena itu saya pikir "info selengkapnya" Anda menimbulkan kebingungan.
Stewart
1
@Stewart: Dia segera menjelaskan bahwa dia tidak bermaksud bahwa seluruh objek disalin.
LarsH
10
@Stewart tidak, objek tidak diteruskan oleh referensi, melainkan referensi objek yang diteruskan oleh nilai. Ini perbedaan penting - lewat referensi berarti bahwa jika saya memiliki variabel lokal yang merujuk ke objek dan meneruskan variabel itu ke metode lain, metode tersebut akan dapat mengubah objek mana yang dirujuk variabel saya, dan ini jelas bukan sesuatu bisa Anda lakukan di Java. Metode ini dapat mengubah status objek melalui salinan referensinya sendiri tetapi tidak dapat mengubah salinan referensi saya untuk menunjuk ke sesuatu yang lain.
Ian Roberts
2
@Stewart yoda.arachsys.com/csharp/parameters.html adalah artikel bagus yang menjelaskan perbedaan antara meneruskan referensi dan meneruskan referensi berdasarkan nilai, dalam konteks C # yang mendukung keduanya.
Ian Roberts