Periksa parameter yang dijelaskan dengan @Nonnull untuk null?

19

Kami telah mulai menggunakan FindBugs dan menjelaskan parameter kami dengan @Nonnulltepat, dan berfungsi baik untuk menunjukkan bug di awal siklus. Sejauh ini kami terus memeriksa argumen ini untuk nullmenggunakan Guava checkNotNull, tapi saya lebih suka untuk memeriksa nullhanya di tepi - tempat-tempat di mana nilainya dapat masuk tanpa diperiksa null, misalnya, permintaan SOAP.

// service layer accessible from outside
public Person createPerson(@CheckForNull String name) {
    return new Person(Preconditions.checkNotNull(name));
}

...

// internal constructor accessed only by the service layer
public Person(@Nonnull String name) {
    this.name = Preconditions.checkNotNull(name);  // remove this check?
}

Saya mengerti bahwa @Nonnullitu tidak memblokir nullnilai-nilai itu sendiri.

Namun, mengingat bahwa FindBugs akan menunjukkan di mana saja nilai ditransfer dari bidang yang tidak ditandai ke yang ditandai @Nonnull, tidak dapatkah kita bergantung padanya untuk menangkap kasus ini (yang memang terjadi) tanpa harus memeriksa nilai-nilai ini untuk di nullmana saja mereka diedarkan di sistem? Apakah saya naif ingin mempercayai alat ini dan menghindari cek verbose ini?

Intinya: Meskipun tampaknya aman untuk menghapus nullpemeriksaan kedua di bawah, apakah ini praktik yang buruk?

Pertanyaan ini mungkin terlalu mirip dengan Haruskah seseorang memeriksa nol jika dia tidak mengharapkan nol , tapi saya bertanya secara spesifik sehubungan dengan @Nonnullanotasi.

David Harkness
sumber

Jawaban:

6

Jika Anda tidak mempercayai FindBug, ini bisa menjadi opsi untuk menggunakan pernyataan. Dengan cara ini Anda menghindari masalah yang disebutkan oleh Pengguna MSalters tetapi masih harus diperiksa. Tentu saja, pendekatan ini mungkin tidak dapat diterapkan jika pendekatan gagal-cepat semacam itu tidak layak, yaitu Anda harus menangkap pengecualian apa pun.

Dan untuk selanjutnya menjawab pertanyaan apakah itu praktik yang buruk. Yah, ini bisa diperdebatkan tetapi saya tidak akan berpikir begitu. Saya menganggap anotasi FindBug ini sebagai spesifikasi ringan mengikuti desain dengan prinsip kontrak.

Dalam desain dengan kontrak, Anda membuat kontrak antara metode dan peneleponnya. Kontrak terdiri dari prasyarat yang pemanggil setuju untuk penuhi ketika memanggil metode dan postkondisi yang pada gilirannya dipenuhi oleh metode setelah eksekusi jika prasyaratnya dipenuhi.

Salah satu manfaat dari metode pengembangan ini adalah Anda dapat menghindari apa yang disebut gaya pemrograman defensif (di mana Anda memeriksa semua nilai parameter yang tidak valid, dll.). Alih-alih, Anda dapat mengandalkan kontrak, dan menghindari cek berlebihan untuk meningkatkan kinerja dan kesesuaian.

Kontrak dapat digunakan untuk pengecekan runtime-assertion yang mengikuti prinsip gagal-pertama yang disebutkan di atas di mana Anda ingin program Anda gagal jika kontrak rusak, memberi Anda petunjuk untuk mengidentifikasi sumber kesalahan. (Prasyarat yang gagal berarti bahwa penelepon melakukan sesuatu yang salah, postkondisi yang gagal menunjukkan kesalahan implementasi dari metode yang diberikan)

Selain itu, kontrak dapat digunakan untuk analisis statis, yang mencoba menarik kesimpulan tentang kode sumber tanpa benar-benar menjalankannya.

Anotasi @nonnull dapat dilihat sebagai prasyarat yang digunakan untuk analisis statis oleh alat FindBugs. Jika Anda lebih suka pendekatan seperti pemeriksaan runtime-pernyataan, yaitu strategi gagal pertama, Anda harus mempertimbangkan menggunakan pernyataan pernyataan sebagai Java bawaan. Atau mungkin untuk beradaptasi dengan strategi spesifikasi yang lebih canggih menggunakan bahasa spesifikasi antarmuka antarmuka seperti Java Modeling Language (JML).

JML adalah ekstensi dari Java di mana spesifikasi tertanam dalam Komentar Java khusus. Keuntungan dari pendekatan ini adalah bahwa Anda dapat menggunakan spesifikasi yang canggih, bahkan menggunakan pendekatan pengembangan di mana Anda hanya mengandalkan spesifikasi daripada detail implementasi dan menggunakan alat yang berbeda yang menggunakan spesifikasi, seperti alat pengecekan runtime-assertion yang disebutkan, pembuatan unit-test atau dokumentasi otomatis.

Jika Anda berbagi pandangan saya tentang @nonnull menjadi kontrak (ringan), itu akan menjadi praktik umum untuk tidak menambahkan cek tambahan karena ide asli di balik prinsip ini adalah untuk menghindari pemrograman defensif.

FabianB
sumber
2
Ini persis bagaimana saya menggunakan @Nonnull: sebagai spesifikasi kontrak. Saya telah menghapus cek defensif di belakang kontrak dan sejauh ini tidak mengalami kesulitan.
David Harkness
4

Nah, pertimbangkan mengapa Anda tidak menulis

this.name = Preconditions.checkNotNull(name);  // Might be necessary
that.name = Preconditions.checkNotNull(this.name);  // Who knows?

Pemrograman bukanlah ilmu hitam. Variabel tidak tiba-tiba menjadi Null. Jika Anda tahu variabel tidak Null, dan Anda tahu itu tidak berubah, maka jangan tidak memeriksa.

Jika Anda memeriksanya, beberapa programmer di masa depan (mungkin Anda) akan menghabiskan banyak waktu mencari tahu mengapa cek itu ada. Lagipula, cek itu pasti ada karena suatu alasan, jadi apa yang Anda abaikan?

MSalters
sumber
3
The @Nonnull, ditetapkan kontrak yang penelepon tidak harus lulus nullke konstruktor, dan FindBugs menggunakan analisis statis untuk menemukan panggilan yang mungkin memungkinkan null--specifically panggilan yang melewati nilai tidak ditandai @Nonnullsendiri. Tetapi seorang penelepon bebas untuk menyampaikan nulldan mengabaikan peringatan dari FindBugs.
David Harkness
Pemrograman idealnya bukan ilmu hitam. Sayangnya, peringatan dan kejutan berlimpah, terutama ketika bekerja dengan beberapa kode konkurensi di luar sana ♬ ~ ᕕ (ᐛ) ᕗ
Tim Harper