Beberapa waktu yang lalu saya menulis jawaban ini untuk pertanyaan tentang bagaimana menghindari memiliki pengambil dan penyetel untuk setiap variabel yang bisa berubah. Pada saat itu, saya hanya punya firasat yang sulit untuk diverbalkan bahwa ini adalah Ide yang Buruk, tetapi OP secara eksplisit menanyakan bagaimana cara melakukannya. Saya mencari di sini tentang mengapa ini mungkin menjadi masalah, dan menemukan pertanyaan ini , yang jawabannya tampaknya menganggap bahwa menggunakan refleksi kecuali benar-benar diperlukan adalah praktik yang buruk.
Jadi, dapatkah seseorang mengatakan secara verbal mengapa refleksi harus dihindari, khususnya dalam kasus pengambil dan penyetel generik?
java
reflection
HAEM
sumber
sumber
Map
'sget
danput
, yang merupakan cara yang cukup kikuk dalam melakukan sesuatu.Jawaban:
Kerugian refleksi secara umum
Refleksi lebih sulit dipahami daripada kode garis lurus.
Dalam pengalaman saya, refleksi adalah fitur "tingkat ahli" di Jawa. Saya berpendapat bahwa sebagian besar programmer tidak pernah menggunakan refleksi secara aktif (mis. Mengkonsumsi perpustakaan yang menggunakan refleksi tidak masuk hitungan). Itu membuat kode menggunakannya lebih sulit untuk dipahami oleh para programmer ini.
Kode refleksi tidak dapat diakses untuk analisis statis
Misalkan saya memiliki seorang pengambil
getFoo
di kelas saya dan saya ingin mengubah nama menjadigetBar
. Jika saya tidak menggunakan refleksi, saya bisa mencari basis kodegetFoo
dan akan menemukan setiap tempat yang menggunakan pengambil sehingga saya dapat memperbaruinya, dan bahkan jika saya melewatkannya, kompiler akan mengeluh.Tetapi jika tempat yang menggunakan pengambil adalah sesuatu seperti
callGetter("Foo")
dancallGetter
tidakgetClass().getMethod("get"+name).invoke(this)
, maka metode di atas tidak akan menemukannya, dan kompiler tidak akan mengeluh. Hanya ketika kode tersebut benar-benar dieksekusi Anda akan mendapatkanNoSuchMethodException
. Dan bayangkan rasa sakit yang Anda alami jika pengecualian itu (yang dilacak) ditelan olehcallGetter
karena "itu hanya digunakan dengan string hard-coded, itu tidak dapat benar-benar terjadi". (Tidak ada yang akan melakukan itu, seseorang mungkin berdebat? Kecuali bahwa OP melakukan hal itu dalam jawaban SO-nya. Jika bidang ini diganti namanya, pengguna setter generik tidak akan pernah melihat, kecuali bug yang sangat tidak jelas dari setter diam-diam tidak melakukan apa-apa. Pengguna pengambil mungkin, jika mereka beruntung, memperhatikan output konsol dari pengecualian yang diabaikan.)Kode refleksi bukan tipe-diperiksa oleh kompiler
Ini pada dasarnya adalah sub-poin besar di atas. Tentang kode refleksi
Object
. Jenis diperiksa pada saat runtime. Kesalahan ditemukan oleh unit test, tetapi hanya jika Anda memiliki jangkauan. ("Ini hanya pengambil, saya tidak perlu mengujinya.") Pada dasarnya, Anda kehilangan keuntungan menggunakan Java lebih dari Python mendapatkan Anda di tempat pertama.Kode refleksi tidak tersedia untuk optimasi
Mungkin tidak dalam teori, tetapi dalam praktiknya, Anda tidak akan menemukan JVM yang inline atau membuat cache inline untuk
Method.invoke
. Panggilan metode normal tersedia untuk optimasi seperti itu. Itu membuat mereka jauh lebih cepat.Kode pantulan umumnya lambat
Pencarian metode dinamis dan pemeriksaan jenis yang diperlukan untuk kode refleksi lebih lambat daripada panggilan metode normal. Jika Anda mengubah rajutan satu baris murah itu menjadi binatang pantulan, Anda mungkin (saya belum mengukur ini) akan melihat beberapa urutan besarnya perlambatan.
Kekurangan dari pengambil / penyetel generik secara khusus
Itu hanya ide yang buruk, karena kelas Anda sekarang tidak memiliki enkapsulasi lagi. Setiap bidang yang dimilikinya dapat diakses. Anda mungkin juga membuat semuanya publik.
sumber
Karena pass-through getter / setters adalah kekejian yang tidak memberikan nilai. Mereka tidak menyediakan enkapsulasi karena pengguna dapat memperoleh nilai aktual melalui properti. Mereka adalah YAGNI karena Anda jarang jika akan mengubah implementasi penyimpanan bidang Anda.
Dan karena ini jauh lebih sedikit performant. Setidaknya dalam C #, pengambil / penyetel akan langsung sejalan dengan satu instruksi CIL. Dalam jawaban Anda, Anda menggantinya dengan 3 panggilan fungsi, yang pada gilirannya perlu memuat metadata untuk direfleksikan. Saya biasanya menggunakan 10x sebagai aturan praktis untuk biaya refleksi, tetapi ketika saya mencari data, salah satu jawaban pertama termasuk jawaban ini yang menyempitnya mendekati 1000x.
(Dan itu membuat kode Anda lebih sulit untuk di-debug, dan itu merusak kegunaan karena ada lebih banyak cara untuk menyalahgunakannya, dan itu merusak pemeliharaan karena Anda sekarang menggunakan string ajaib daripada pengidentifikasi yang tepat ...)
sumber
getX()
dansetX()
metode yang dapat ditemukan melalui refleksi. Kacang tidak selalu seharusnya merangkum nilai-nilai mereka, mereka mungkin hanya DTO. Jadi di Jawa, getter sering kali terasa sakit. Properti C # jauh, jauh lebih baik dalam segala hal.Selain argumen yang sudah disajikan.
Itu tidak menyediakan antarmuka yang diharapkan.
Pengguna berharap untuk memanggil foo.getBar () dan foo.setBar (nilai), bukan foo.get ("bar") dan foo.set ("bar", nilai)
Untuk menyediakan antarmuka yang diharapkan pengguna, Anda perlu menulis pengambil / penyetel terpisah untuk setiap properti. Setelah Anda selesai melakukannya, tidak ada gunanya menggunakan refleksi.
sumber
Tentu saja itu bukan ide yang buruk, itu hanya daripada di dunia java yang normal adalah ide yang buruk (ketika Anda hanya menginginkan pengambil atau penyetel). Misalnya beberapa frameworks (spring mvc) atau librares sudah menggunakannya (jstl) dengan cara itu, beberapa parser json o xml menggunakannya, jika Anda ingin menggunakan DSL Anda akan menggunakannya. Maka itu tergantung pada skenario use case Anda.
Tidak ada yang benar atau salah sebagai fakta.
sumber