Sebuah layanan web mengembalikan XML yang sangat besar dan saya perlu mengakses bidang bersarangnya secara mendalam. Sebagai contoh:
return wsObject.getFoo().getBar().getBaz().getInt()
Masalahnya adalah bahwa getFoo()
, getBar()
, getBaz()
mungkin semua kembali null
.
Namun, jika saya memeriksa null
dalam semua kasus, kodenya menjadi sangat bertele-tele dan sulit dibaca. Selain itu, saya mungkin melewatkan pemeriksaan untuk beberapa bidang.
if (wsObject.getFoo() == null) return -1;
if (wsObject.getFoo().getBar() == null) return -1;
// maybe also do something with wsObject.getFoo().getBar()
if (wsObject.getFoo().getBar().getBaz() == null) return -1;
return wsObject.getFoo().getBar().getBaz().getInt();
Apakah menulis itu dapat diterima
try {
return wsObject.getFoo().getBar().getBaz().getInt();
} catch (NullPointerException ignored) {
return -1;
}
atau apakah itu dianggap antipattern?
java
exception
nullpointerexception
null
custom-error-handling
David Frank
sumber
sumber
null
cek sebanyak itu, karenawsObject.getFoo().getBar().getBaz().getInt()
sudah menjadi bau kode. Baca apa itu "Law of Demeter" dan pilih untuk memfaktor ulang kode Anda sesuai dengan itu. Kemudian masalah dengannull
pemeriksaan juga akan hilang. Dan pikirkan tentang menggunakanOptional
.wsdl2java
, yang tidak menghormati Hukum Demeter.Jawaban:
Penangkapan
NullPointerException
adalah hal yang sangat bermasalah untuk dilakukan karena bisa terjadi hampir di mana saja. Sangat mudah untuk mendapatkannya dari bug, menangkapnya secara tidak sengaja dan melanjutkan seolah-olah semuanya normal, sehingga menyembunyikan masalah nyata. Ini sangat sulit untuk ditangani jadi yang terbaik adalah menghindari sama sekali. (Misalnya, pikirkan tentang unboxing otomatis dari sebuah nullInteger
.)Saya menyarankan agar Anda menggunakan
Optional
kelas sebagai gantinya. Ini sering kali merupakan pendekatan terbaik ketika Anda ingin bekerja dengan nilai-nilai yang ada atau tidak ada.Menggunakan itu Anda bisa menulis kode Anda seperti ini:
Mengapa opsional?
Menggunakan
Optional
s, bukannull
untuk nilai yang mungkin tidak ada membuat fakta itu sangat terlihat dan jelas bagi pembaca, dan sistem tipe akan memastikan Anda tidak sengaja melupakannya.Anda juga mendapatkan akses ke metode untuk bekerja dengan nilai-nilai seperti itu dengan lebih nyaman, seperti
map
danorElse
.Apakah ketiadaan valid atau error?
Tetapi juga pikirkan apakah itu adalah hasil yang valid untuk metode perantara untuk mengembalikan nol atau jika itu merupakan tanda kesalahan. Jika selalu merupakan kesalahan maka mungkin lebih baik melempar pengecualian daripada mengembalikan nilai khusus, atau metode perantara itu sendiri membuat pengecualian.
Mungkin lebih banyak pilihan?
Jika di sisi lain nilai yang tidak ada dari metode perantara valid, mungkin Anda dapat beralih ke
Optional
s untuk mereka?Kemudian Anda bisa menggunakannya seperti ini:
Mengapa tidak opsional?
Satu-satunya alasan yang dapat saya pikirkan untuk tidak menggunakan
Optional
adalah jika ini benar-benar merupakan bagian penting dari kinerja kode, dan jika overhead pengumpulan sampah ternyata menjadi masalah. Ini karena beberapaOptional
objek dialokasikan setiap kali kode dijalankan, dan VM mungkin tidak dapat mengoptimalkannya. Dalam hal ini jika-tes Anda mungkin lebih baik.sumber
Try
bukanOptional
. Meskipun tidak adaTry
di Java API, ada banyak lib yang menyediakannya, misalnya javaslang.io , github.com/bradleyscollins/try4j , functionaljava.org atau github.com/jasongoodwin/better-java-monadsFClass::getBar
dll akan lebih pendek.static
metode, yang akan menimbulkan hukuman yang sangat kecil.Saya menyarankan untuk mempertimbangkan
Objects.requireNonNull(T obj, String message)
. Anda dapat membuat rantai dengan pesan mendetail untuk setiap pengecualian, sepertiSaya menyarankan Anda untuk tidak menggunakan nilai-kembali khusus, seperti
-1
. Itu bukan gaya Java. Java telah merancang mekanisme pengecualian untuk menghindari cara kuno yang berasal dari bahasa C.Melempar
NullPointerException
juga bukan pilihan terbaik. Anda dapat memberikan pengecualian Anda sendiri (membuatnya dicentang untuk menjamin bahwa itu akan ditangani oleh pengguna atau tidak dicentang untuk memprosesnya dengan cara yang lebih mudah) atau menggunakan pengecualian tertentu dari pengurai XML yang Anda gunakan.sumber
Objects.requireNonNull
akhirnya melemparNullPointerException
. Jadi ini tidak membuat situasinya berbeda darireturn wsObject.getFoo().getBar().getBaz().getInt()
if
seperti yang ditunjukkan OPOptional
kelas, atau mungkin mengembalikan nullableInteger
Dengan asumsi struktur kelas benar-benar di luar kendali kami, seperti yang terjadi, saya pikir menangkap NPE seperti yang disarankan dalam pertanyaan memang solusi yang masuk akal, kecuali kinerja menjadi perhatian utama. Satu perbaikan kecil mungkin untuk membungkus logika lempar / tangkap untuk menghindari kekacauan:
Sekarang Anda cukup melakukan:
sumber
return get(() -> wsObject.getFoo().getBar().getBaz().getInt(), "");
tidak memberikan kesalahan dalam waktu kompilasi yang bisa menjadi masalah.Seperti yang sudah ditunjukkan oleh Tom di komentarnya,
Pernyataan berikut tidak mematuhi Hukum Demeter ,
Apa yang Anda inginkan adalah
int
dan Anda bisa mendapatkannyaFoo
. Law of Demeter mengatakan, jangan pernah berbicara dengan orang asing . Untuk kasus Anda, Anda dapat menyembunyikan implementasi aktual di bawah tendaFoo
danBar
.Sekarang, Anda dapat membuat metode dalam
Foo
untuk mengambilint
dariBaz
. Pada akhirnya,Foo
akan memilikiBar
danBar
kita dapat mengaksesInt
tanpa mengeksposBaz
langsung keFoo
. Jadi, pemeriksaan null mungkin dibagi ke kelas yang berbeda dan hanya atribut yang diperlukan yang akan dibagikan di antara kelas.sumber
null
pemeriksaan sub tag-nya sendiri.Jawaban saya hampir sama dengan @janki, tetapi saya ingin sedikit mengubah cuplikan kodenya seperti di bawah ini:
Anda juga dapat menambahkan pemeriksaan null
wsObject
, jika ada kemungkinan objek tersebut menjadi null.sumber
Anda mengatakan bahwa beberapa metode "mungkin kembali
null
" tetapi tidak mengatakan dalam keadaan apa metode tersebut kembalinull
. Anda mengatakan Anda menangkapnyaNullPointerException
tetapi Anda tidak mengatakan mengapa Anda menangkapnya. Kurangnya informasi ini menunjukkan bahwa Anda tidak memiliki pemahaman yang jelas tentang apa pengecualian itu dan mengapa mereka lebih unggul daripada alternatif.Pertimbangkan metode kelas yang dimaksudkan untuk melakukan suatu tindakan, tetapi metode tersebut tidak dapat menjamin akan melakukan tindakan tersebut, karena keadaan di luar kendalinya (yang sebenarnya merupakan kasus untuk semua metode di Java ). Kami memanggil metode itu dan kembali. Kode yang memanggil metode itu perlu mengetahui apakah itu berhasil. Bagaimana dia bisa tahu? Bagaimana itu bisa disusun untuk mengatasi dua kemungkinan, sukses atau gagal?
Menggunakan pengecualian, kita dapat menulis metode yang berhasil sebagai kondisi posting . Jika metode kembali, itu berhasil. Jika mengeluarkan pengecualian, itu gagal. Ini adalah kemenangan besar untuk kejelasan. Kita dapat menulis kode yang memproses kasus normal, kasus sukses, dan memindahkan semua kode penanganan kesalahan ke dalam
catch
klausa dengan jelas. Sering kali terjadi bahwa detail tentang bagaimana atau mengapa suatu metode tidak berhasil tidak penting bagi pemanggil, jadicatch
klausa yang sama dapat digunakan untuk menangani beberapa jenis kegagalan. Dan sering terjadi bahwa metode tidak perlu pengecualian menangkap sama sekali , tapi hanya dapat memungkinkan mereka untuk menyebarkan ke nya pemanggil. Pengecualian karena bug program ada di kelas terakhir itu; beberapa metode dapat bereaksi dengan tepat saat ada bug.Jadi, metode itu kembali
null
.null
nilai menunjukkan bug dalam kode Anda? Jika ya, Anda tidak boleh menangkap pengecualian sama sekali. Dan kode Anda tidak boleh mencoba menebak-nebak sendiri. Tulis saja apa yang jelas dan ringkas dengan asumsi itu akan berhasil. Apakah rangkaian panggilan metode jelas dan ringkas? Kemudian gunakan saja.null
nilai menunjukkan input yang tidak valid untuk program Anda? Jika ya, aNullPointerException
bukanlah pengecualian yang tepat untuk dilemparkan, karena secara konvensional itu disediakan untuk menunjukkan bug. Anda mungkin ingin menampilkan pengecualian khusus yang diturunkan dariIllegalArgumentException
(jika Anda ingin pengecualian tidak dicentang ) atauIOException
(jika Anda ingin pengecualian dicentang). Apakah program Anda diharuskan untuk memberikan pesan kesalahan sintaks yang terperinci ketika ada input yang tidak valid? Jika demikian, memeriksa setiap metode untuknull
nilai kembalian kemudian melemparkan pengecualian diagnostik yang sesuai adalah satu-satunya hal yang dapat Anda lakukan. Jika program Anda tidak perlu menyediakan diagnosis mendetail, merangkai panggilan metode bersama-sama, menangkap apa sajaNullPointerException
, lalu membuang pengecualian khusus Anda adalah yang paling jelas dan ringkas.Salah satu jawaban mengklaim bahwa panggilan metode berantai melanggar Hukum Demeter dan karenanya buruk. Klaim itu salah.
sumber
Untuk meningkatkan keterbacaan, Anda mungkin ingin menggunakan banyak variabel, seperti
sumber
Jangan tangkap
NullPointerException
. Anda tidak tahu dari mana asalnya (saya tahu itu tidak mungkin terjadi dalam kasus Anda, tetapi mungkin ada hal lain yang melemparkannya) dan lambat. Anda ingin mengakses bidang yang ditentukan dan untuk ini setiap bidang lainnya tidak boleh kosong. Ini adalah alasan valid yang sempurna untuk memeriksa setiap bidang. Saya mungkin akan memeriksanya di satu jika dan kemudian membuat metode untuk keterbacaan. Seperti yang ditunjukkan orang lain sudah kembali -1 adalah sekolah yang sangat tua tetapi saya tidak tahu apakah Anda punya alasan untuk itu atau tidak (misalnya berbicara dengan sistem lain).Sunting: Ini bisa diperdebatkan jika tidak mematuhi Law Of Demeter karena WsObject mungkin hanya sebuah struktur data (periksa https://stackoverflow.com/a/26021695/1528880 ).
sumber
Jika Anda tidak ingin memfaktor ulang kode dan Anda dapat menggunakan Java 8, Anda dapat menggunakan referensi Metode.
Demo sederhana terlebih dahulu (maafkan kelas dalam statis)
Keluaran
Antarmuka
Getter
hanyalah antarmuka fungsional, Anda dapat menggunakan yang setara.GetterResult
kelas, pengakses dilucuti untuk kejelasan, memegang hasil dari rantai pengambil, jika ada, atau indeks pengambil terakhir yang dipanggil.Metode
getterChain
ini adalah potongan kode sederhana yang dapat dibuat secara otomatis (atau secara manual jika diperlukan).Saya menyusun kodenya sehingga blok yang berulang menjadi bukti dengan sendirinya.
Ini bukan solusi yang tepat karena Anda masih perlu menentukan satu kelebihan muatan
getterChain
per jumlah getter.Saya akan merefaktor kode sebagai gantinya, tetapi jika tidak bisa dan Anda menemukan diri Anda menggunakan rantai pengambil panjang sering Anda dapat mempertimbangkan untuk membangun kelas dengan beban berlebih yang mengambil dari 2 menjadi, katakanlah, 10, pengambil.
sumber
Seperti yang dikatakan orang lain, menghormati Hukum Demeter jelas merupakan bagian dari solusi. Bagian lain, jika memungkinkan, adalah mengubah metode yang dirantai tersebut sehingga tidak dapat kembali
null
. Anda dapat menghindari kembalinull
dengan mengembalikan objek kosongString
, kosongCollection
, atau objek dummy lain yang berarti atau melakukan apa pun yang akan dilakukan pemanggilnull
.sumber
Saya ingin menambahkan jawaban yang berfokus pada arti kesalahan . Pengecualian nol itu sendiri tidak memberikan arti penuh kesalahan. Jadi saya menyarankan untuk menghindari berurusan dengan mereka secara langsung.
Ada ribuan kasus di mana kode Anda bisa salah: tidak dapat terhubung ke database, IO Exception, Network error ... Jika Anda menanganinya satu per satu (seperti centang null di sini), itu akan terlalu merepotkan.
Di dalam kode:
Bahkan ketika Anda tahu bidang mana yang nol, Anda tidak tahu apa yang salah. Mungkin Bar adalah null, tetapi apakah itu diharapkan? Atau apakah itu kesalahan data? Pikirkan tentang orang yang membaca kode Anda
Seperti dalam jawaban xenteros, saya akan mengusulkan menggunakan pengecualian khusus yang tidak dicentang . Misalnya, dalam situasi ini: Foo bisa menjadi null (data valid), tetapi Bar dan Baz tidak boleh null (data tidak valid)
Kode dapat ditulis ulang:
sumber
NullPointerException
adalah pengecualian waktu proses, jadi secara umum tidak disarankan untuk menangkapnya, tetapi untuk menghindarinya.Anda harus menangkap pengecualian di mana pun Anda ingin memanggil metode (atau itu akan menyebarkan tumpukan). Namun demikian, jika dalam kasus Anda, Anda dapat terus bekerja dengan hasil itu dengan nilai -1 dan Anda yakin itu tidak akan menyebar karena Anda tidak menggunakan salah satu "potongan" yang mungkin nol, maka tampaknya tepat bagi saya untuk tangkap
Edit:
Saya setuju dengan jawaban selanjutnya dari @xenteros, akan lebih baik untuk meluncurkan pengecualian Anda sendiri daripada mengembalikan -1 Anda dapat memanggilnya
InvalidXMLException
misalnya.sumber
Telah mengikuti posting ini sejak kemarin.
Saya telah mengomentari / memberi suara pada komentar yang mengatakan, menangkap NPE itu buruk. Inilah mengapa saya melakukan itu.
Keluaran
3.216
0,002
Saya melihat pemenang yang jelas di sini. Memiliki pemeriksaan if jauh lebih murah daripada menangkap pengecualian. Saya telah melihat cara kerja Java-8 itu. Mengingat 70% dari aplikasi saat ini masih berjalan di Java-7, saya menambahkan jawaban ini.
Intinya Untuk aplikasi misi kritis apa pun, menangani NPE itu mahal.
sumber
Jika efisiensi menjadi masalah maka opsi 'tangkap' harus dipertimbangkan. Jika 'catch' tidak dapat digunakan karena akan menyebar (seperti yang disebutkan oleh 'SCouto') maka gunakan variabel lokal untuk menghindari beberapa panggilan ke metode
getFoo()
,getBar()
dangetBaz()
.sumber
Sebaiknya pertimbangkan untuk membuat Pengecualian Anda sendiri. Sebut saja MyOperationFailedException. Anda dapat membuangnya alih-alih mengembalikan nilai. Hasilnya akan sama - Anda akan keluar dari fungsinya, tetapi Anda tidak akan mengembalikan nilai hard-code -1 yang merupakan anti-pola Java. Di Jawa kami menggunakan Pengecualian.
EDIT:
Menurut diskusi di komentar izinkan saya menambahkan sesuatu ke pemikiran saya sebelumnya. Dalam kode ini ada dua kemungkinan. Salah satunya adalah Anda menerima nol dan yang lainnya adalah, bahwa itu adalah kesalahan.
Jika ini adalah kesalahan dan itu terjadi, Anda dapat men-debug kode Anda menggunakan struktur lain untuk tujuan debugging ketika breakpoint tidak cukup.
Jika dapat diterima, Anda tidak peduli di mana null ini muncul. Jika ya, Anda pasti tidak boleh merantai permintaan tersebut.
sumber
Metode yang Anda miliki sangat panjang, tetapi sangat mudah dibaca. Jika saya adalah pengembang baru yang datang ke basis kode Anda, saya dapat melihat apa yang Anda lakukan dengan cukup cepat. Sebagian besar jawaban lain (termasuk menangkap pengecualian) tampaknya tidak membuat segala sesuatunya lebih mudah dibaca dan beberapa membuatnya kurang dapat dibaca menurut saya.
Mengingat bahwa Anda kemungkinan tidak memiliki kendali atas sumber yang dihasilkan dan dengan asumsi Anda benar-benar hanya perlu mengakses beberapa bidang bersarang di sana-sini, saya akan merekomendasikan untuk membungkus setiap akses yang sangat bersarang dengan sebuah metode.
Jika Anda menemukan diri Anda menulis banyak metode ini atau jika Anda tergoda untuk membuat metode statis publik ini maka saya akan membuat model objek terpisah, bersarang seperti yang Anda inginkan, hanya dengan bidang yang Anda pedulikan, dan mengonversi dari web model objek layanan ke model objek Anda.
Saat Anda berkomunikasi dengan layanan web jarak jauh, biasanya memiliki "domain jauh" dan "domain aplikasi" dan beralih di antara keduanya. Domain jarak jauh sering kali dibatasi oleh protokol web (misalnya, Anda tidak dapat mengirim metode pembantu bolak-balik dalam layanan RESTful murni dan model objek bertingkat sangat umum untuk menghindari beberapa panggilan API) sehingga tidak ideal untuk penggunaan langsung di klien Anda.
Sebagai contoh:
sumber
dengan menerapkan Hukum Demeter,
sumber
Memberi jawaban yang nampaknya berbeda dari yang lainnya.
Alasan:
Untuk membuat kode Anda mudah dibaca, coba ini untuk memeriksa ketentuan:
EDIT:
Setiap saran akan dihargai .. !!
sumber
wsObject
akan berisi nilai yang dikembalikan dari Webservice .. !! Service akan dipanggil danwsObject
akan mendapatkanXML
data yang panjang sebagai respon webservice .. !! Jadi tidak ada yang seperti server yang terletak di benua lain karenagetFoo()
hanyalah sebuah metode pengambil elemen bukan panggilan Webservice .. !! @xenterosSaya menulis kelas yang disebut
Snag
yang memungkinkan Anda menentukan jalur untuk menavigasi melalui pohon objek. Berikut ini contoh penggunaannya:Artinya, instance tersebut
ENGINE_NAME
akan secara efektif memanggilCar?.getEngine()?.getName()
instance yang diteruskan kepadanya, dan dikembalikannull
jika ada referensi yang dikembalikannull
:Ini tidak dipublikasikan di Maven tetapi jika ada yang menganggap ini berguna, ini ada di sini (tentu saja tanpa jaminan!)
Ini agak mendasar tetapi tampaknya melakukan pekerjaan itu. Jelas itu lebih usang dengan versi Java yang lebih baru dan bahasa JVM lain yang mendukung navigasi aman atau
Optional
.sumber