
Hai, habrozhiteli! Keuntungan dari aplikasi modern adalah solusi canggih, termasuk layanan microser, arsitektur reaktif, dan streaming data. Ekspresi Lambda, aliran data, dan sistem modul modul Java yang telah lama dinanti sangat menyederhanakan implementasinya.
Buku ini akan membantu Anda mempelajari fitur baru add-on modern, seperti Streams API dan sistem modul platform Java. Temukan pendekatan baru untuk daya saing dan pelajari bagaimana konsep fungsi meningkatkan kerja dengan kode.
Dalam buku ini: • Fitur Java Baru • Streaming data dan pemrograman reaktif • Sistem modul platform Java.
Kutipan. Bab 11. Kelas opsional sebagai alternatif terbaik untuk null
Angkat tangan Anda jika selama karier Anda sebagai pengembang Java Anda pernah menerima NullPointerException. Biarkan terangkat jika pengecualian ini adalah yang paling umum yang Anda temui. Sayangnya, kami tidak melihat Anda sekarang, tetapi sangat mungkin tangan Anda terangkat. Kami juga curiga bahwa Anda mungkin berpikir sesuatu seperti: “Ya, saya setuju. Pengecualian NullPointerException adalah sakit kepala untuk setiap pengembang Java, apakah itu pemula atau ahli. Tapi bagaimanapun juga tidak ada yang bisa dilakukan dengan mereka, ini adalah harga yang kami bayar untuk menggunakan desain yang nyaman dan mungkin tak terhindarkan sebagai tautan kosong. ” Ini adalah pendapat umum di dunia pemrograman (imperatif); namun demikian, mungkin ini bukan keseluruhan kebenaran, melainkan prasangka yang mengakar dalam.
Spesialis ilmu komputer Inggris Tony Hoare, yang membuat tautan nol pada tahun 1965, saat mengembangkan bahasa ALGOL W, salah satu bahasa pemrograman yang diketik pertama dengan catatan yang memorinya dialokasikan pada heap, kemudian mengakui bahwa ia melakukan ini “ hanya karena kemudahan implementasi. " Meskipun dia ingin menjamin "keamanan penuh menggunakan pengecualian untuk tautan kosong, dia berpikir bahwa ini adalah cara yang paling nyaman untuk mensimulasikan ketiadaan nilai. Bertahun-tahun kemudian, dia menyesali keputusan ini, menyebutnya "kesalahan miliaran dolar saya." Kita semua melihat hasil dari keputusan ini. Misalnya, kita dapat memeriksa bidang objek untuk menentukan apakah itu mewakili salah satu dari dua nilai yang mungkin, hanya untuk menemukan bahwa kita memeriksa bukan objek, tetapi pointer nol, dan segera mendapatkan NullPointerException yang menjengkelkan ini.
Bahkan, Hoar mungkin telah meremehkan biaya untuk memperbaiki jutaan pengembang kesalahan yang disebabkan oleh tautan kosong selama 50 tahun terakhir. Memang, sebagian besar bahasa pemrograman1 dibuat selama beberapa dekade terakhir, termasuk Jawa, didasarkan pada keputusan desain yang sama, mungkin karena alasan kompatibilitas dengan bahasa yang lebih tua atau (lebih mungkin), seperti kata Hoar, “hanya karena kemudahan implementasi ". Kami mulai dengan menunjukkan contoh sederhana masalah yang dihadapi saat menggunakan null.
11.1. Cara mensimulasikan kurangnya nilai
Bayangkan Anda memiliki struktur objek bersarang di Listing 11.1 untuk pemilik mobil yang membeli asuransi mobil.
Listing 11.1. Model Data Orang / Mobil / Asuransi
public class Person { private Car car; public Car getCar() { return car; } } public class Car { private Insurance insurance; public Insurance getInsurance() { return insurance; } } public class Insurance { private String name; public String getName() { return name; } }
Menurut Anda apa masalahnya dengan kode berikut?
public String getCarInsuranceName(Person person) { return person.getCar().getInsurance().getName(); }
Kode ini terlihat cukup masuk akal, tetapi banyak orang tidak memiliki mobil, jadi apa hasil dari memanggil metode getCar dalam kasus ini? Seringkali (dan sia-sia) mereka mengembalikan tautan kosong untuk menunjukkan tidak adanya nilai (dalam hal ini, untuk menunjukkan tidak adanya mesin). Akibatnya, memanggil metode getInsurance akan mengembalikan asuransi tautan kosong, yang akan menyebabkan NullPointerException dilemparkan pada saat run time dan program berhenti. Tapi itu belum semuanya. Tetapi bagaimana jika objek orang itu nol? Bagaimana jika getInsurance juga dikembalikan nol?
11.1.1. Kurangi NullPointerException dengan Pemeriksaan Keamanan
Bagaimana cara menghindari NullPointerException yang tidak terduga? Anda biasanya dapat menambahkan cek nol di mana pun Anda butuhkan (dan kadang-kadang, melebihi persyaratan pemrograman yang aman, dan di mana Anda tidak perlu), dan seringkali dengan gaya yang berbeda. Upaya pertama kami untuk menulis metode untuk mencegah pembuatan NullPointerException ditunjukkan pada Listing 11.2.
Metode ini memeriksa null setiap kali variabel didereferensi, mengembalikan nilai string "Tidak Diketahui" jika setidaknya salah satu variabel yang ditemui dalam rantai dereferensi ini adalah nilai kosong. Satu-satunya pengecualian untuk aturan ini adalah bahwa kami tidak memeriksa nama perusahaan asuransi untuk nol, karena kami tahu bahwa (seperti perusahaan lain) itu harus memiliki nama. Harap dicatat bahwa kami berhasil menghindari pemeriksaan terakhir ini hanya karena pengetahuan tentang area subjek, tetapi fakta ini tidak tercermin dalam kelas Java yang memodelkan data kami.
Kami menggambarkan metode di Listing 11.2 sebagai “keraguan mendalam,” karena pola berulang terlihat di dalamnya: setiap kali ragu, variabelnya tidak nol, Anda harus menambahkan blok bersarang jika lain, sehingga meningkatkan tingkat indentasi kode. Jelas, teknik ini tidak skala dengan baik dan mengurangi keterbacaan, jadi lebih baik untuk mencoba solusi yang berbeda. Untuk menghindari masalah ini, mari ambil jalur yang berbeda, seperti yang ditunjukkan pada Listing 11.3.
Dalam upaya kedua, kami mencoba menghindari bersarang mendalam jika blok menggunakan strategi yang berbeda: setiap kali kami menemukan variabel nol, kami mengembalikan nilai string "Tidak Dikenal". Tetapi solusi ini juga jauh dari ideal; Sekarang metode ini memiliki empat titik keluar yang berbeda, yang sangat mempersulit pemeliharaannya. Selain itu, nilai default yang dikembalikan untuk null - string "Unknown" - diulangi di tiga tempat dan (kami harap) tidak mengandung kesalahan ejaan! Untuk menghindari ini, Anda bisa, tentu saja, memindahkan string berulang ke konstan.
Selain itu, proses ini rawan kesalahan. Bagaimana jika Anda lupa memeriksa apakah salah satu properti itu nol? Dalam bab ini, kami berpendapat bahwa menggunakan null untuk mewakili kurangnya nilai adalah pendekatan yang secara fundamental salah. Diperlukan cara yang lebih baik untuk memodelkan ketidakhadiran dan keberadaan nilai.
11.1.2. Masalah yang ditemui dengan null
Untuk meringkas hal di atas, penggunaan tautan kosong di Jawa mengarah pada masalah berikut, baik teoretis maupun praktis.
- Berfungsi sebagai sumber kesalahan. NullPointerException adalah pengecualian yang paling umum (dengan margin lebar) di Jawa.
- "Mengembang" kode. Keterbacaan semakin buruk karena kebutuhan untuk mengisi kode dengan cek nol, sering kali bersarang secara mendalam.
- Itu tidak masuk akal. Itu tidak memiliki makna semantik, khususnya, pendekatan semacam itu untuk memodelkan tidak adanya makna dalam bahasa dengan tipifikasi statis pada dasarnya salah.
- Melanggar ideologi bahasa Jawa. Java selalu menyembunyikan pointer dari pengembang, dengan satu pengecualian: pointer nol.
- Menciptakan celah dalam sistem tipe. null tidak termasuk jenis apa pun atau informasi lain, sehingga dapat ditetapkan untuk semua jenis tautan. Ini dapat menyebabkan masalah ketika mentransfer null ke bagian lain dari sistem di mana tidak ada informasi tentang apa yang seharusnya null ini awalnya.
Sebagai dasar untuk solusi layak lainnya di subbagian berikutnya, kami secara singkat memeriksa kemungkinan yang ditawarkan oleh bahasa pemrograman lain.
11.1.3. Alternatif untuk null dalam bahasa pemrograman lain
Dalam beberapa tahun terakhir, bahasa seperti Groovy telah berhasil menghindari masalah ini dengan memperkenalkan operator panggilan aman (?), Yang dirancang untuk bekerja dengan aman dengan nilai nol potensial. Untuk memahami bagaimana proses ini diterapkan dalam praktik, pertimbangkan kode Groovy berikut. Ini mengambil nama perusahaan asuransi di mana orang yang ditentukan mengasuransikan mobil:
def carInsuranceName = person?.car?.insurance?.name
Seharusnya jelas bagi Anda apa yang dilakukan kode ini. Seseorang mungkin tidak memiliki mobil, untuk simulasi yang kami tetapkan nol pada tautan mobil dari objek orang tersebut. Demikian pula, mesin mungkin tidak diasuransikan. Operator panggilan aman Groovy memungkinkan Anda bekerja dengan aman dengan tautan yang berpotensi kosong tanpa membuang NullPointerException, meneruskan tautan kosong ke rantai panggilan dan mengembalikan nol jika nilai apa pun dari rantai itu nol.
Fitur serupa diusulkan untuk diterapkan di Java 7, tetapi kemudian diputuskan untuk tidak melakukan ini. Namun, anehnya, operator panggilan aman tidak benar-benar dibutuhkan di Jawa. Pikiran pertama dari pengembang Java saat menemukan NullPointerException adalah dengan cepat memperbaiki masalah dengan menambahkan pernyataan if untuk memeriksa nol sebelum memanggil metode. Memecahkan masalah dengan cara yang serupa, tanpa mempertimbangkan apakah null dapat diterima dalam situasi khusus ini untuk algoritme atau model data Anda, tidak mengarah pada koreksi, tetapi untuk menyembunyikan kesalahan. Dan selanjutnya untuk pengembang berikutnya (mungkin diri Anda dalam seminggu atau sebulan) akan jauh lebih sulit untuk menemukan dan memperbaiki kesalahan ini. Padahal, Anda baru saja menyapu sampah di bawah karpet. Operator dereferencing nol-aman Groovy hanya sapu yang lebih besar dan lebih kuat, yang dengannya Anda dapat melakukan omong kosong seperti itu tanpa benar-benar mengkhawatirkan konsekuensinya.
Bahasa pemrograman fungsional lainnya, seperti Haskell dan Scala, melihat masalah ini secara berbeda. Haskell memiliki tipe Maybe, yang pada dasarnya merangkum nilai opsional. Objek bertipe Mungkin mengandung nilai dari tipe yang ditentukan atau tidak mengandung apa pun. Haskell tidak memiliki konsep tautan kosong. Dalam Scala, untuk merangkum ada atau tidaknya nilai tipe T, struktur logis yang sama Opsi [T] disediakan, yang akan kita bahas dalam Bab 20. Dalam kasus ini, Anda harus secara eksplisit memeriksa apakah suatu nilai hadir menggunakan operasi dari Opsi jenis, yang menyediakan “pemeriksaan nol” . Sekarang tidak lagi mungkin untuk "lupa untuk memeriksa nol", karena sistem jenis itu sendiri memerlukan pemeriksaan.
Oke, kita sedikit di luar topik, dan itu semua terdengar sangat abstrak. Anda mungkin bertanya-tanya apa yang ditawarkan Java 8 dalam hal ini. Terinspirasi oleh gagasan tentang nilai opsional, para pembuat Java 8 memperkenalkan kelas baru, java.util. Opsional! Dalam bab ini, kami menunjukkan apa keuntungan menggunakannya untuk memodelkan nilai yang mungkin hilang daripada menetapkannya sebagai referensi kosong. Kami juga akan menjelaskan mengapa transisi dari nol ke Opsional mengharuskan programmer untuk meninjau ideologi bekerja dengan nilai-nilai opsional dalam model domain. Akhirnya, kami akan mengeksplorasi kemungkinan kelas Opsional baru ini dan memberikan beberapa contoh praktis penggunaannya yang efektif. Sebagai hasilnya, Anda akan belajar cara mendesain API yang ditingkatkan, di mana pengguna sudah memahami dari tanda tangan metode apakah nilai opsional dimungkinkan di sini.
11.2. Memperkenalkan Kelas Opsional
Di Java 8, di bawah pengaruh bahasa Haskell dan Scala, kelas java.util.Optional baru tampaknya merangkum nilai opsional. Misalnya, jika Anda tahu bahwa seseorang mungkin atau mungkin tidak memiliki mobil, Anda tidak boleh mendeklarasikan mobil variabel di kelas Person dengan tipe Mobil dan menetapkannya sebagai referensi kosong jika orang tersebut tidak memiliki mobil; sebagai gantinya, tipenya harus Opsional, seperti yang ditunjukkan pada gambar. 11.1.

Diberi nilai, kelas Opsional berfungsi sebagai adaptor untuknya. Sebaliknya, tidak adanya nilai dimodelkan menggunakan opsional kosong yang dikembalikan oleh metode Optional.empty. Metode pabrik statis ini mengembalikan contoh tunggal khusus dari kelas opsional. Anda mungkin bertanya-tanya apa perbedaan antara tautan kosong dan Optional.empty (). Semantik, mereka dapat dianggap satu dan sama, tetapi dalam praktiknya ada perbedaan besar di antara mereka. Upaya untuk men-null null akan menyebabkan NullPointerException, dan Optional.empty () adalah objek tipe Optional yang valid dan dapat diterapkan, yang dapat diakses dengan mudah. Segera Anda akan melihat bagaimana tepatnya.
Perbedaan semantik praktis yang penting dalam menggunakan objek opsional, bukan nol: mendeklarasikan variabel tipe opsional dengan jelas mengatakan bahwa nilai kosong diizinkan pada titik ini. Dan sebaliknya, selalu menggunakan tipe Mobil dan, mungkin, kadang-kadang memberikan referensi kosong ke variabel jenis ini, Anda berarti bahwa Anda hanya bergantung pada pengetahuan domain Anda untuk memahami apakah nol milik domain definisi variabel yang diberikan.
Dengan mengingat hal ini, Anda dapat mengulang model asli dari Listing 11.1. Kami menggunakan kelas opsional, seperti yang ditunjukkan pada Listing 11.4.
Perhatikan bahwa menggunakan kelas Opsional memperkaya semantik model. Bidang opsional di kelas Person dan bidang opsional di kelas Mobil mencerminkan fakta bahwa seseorang mungkin atau mungkin tidak memiliki mobil, seperti halnya mesin yang mungkin atau mungkin tidak diasuransikan.
Pada saat yang sama, menyatakan nama perusahaan asuransi sebagai String, bukan Opsional, jelas menunjukkan bahwa perusahaan asuransi harus memiliki nama. Dengan demikian, Anda tahu pasti bahwa Anda akan mendapatkan NullPointerException ketika mendereferensi nama perusahaan asuransi; menambahkan pemeriksaan nol tidak perlu, karena itu hanya akan menyembunyikan masalah. Perusahaan asuransi harus memiliki nama, jadi jika Anda menemukan perusahaan tanpa nama, Anda perlu mencari tahu apa yang salah dengan data tersebut, dan tidak menambahkan sepotong kode untuk menyembunyikan fakta ini.

Penggunaan nilai-nilai opsional yang konsisten menciptakan perbedaan yang jelas antara nilai yang mungkin tidak ada dan nilai yang hilang karena kesalahan dalam algoritma atau masalah dalam data. Penting untuk dicatat bahwa kelas opsional tidak dimaksudkan untuk mengganti semua tautan kosong dengan satu. Tugasnya adalah membantu merancang API yang lebih mudah dipahami, sehingga dengan tanda tangan metode ini dapat dipahami apakah nilai opsional dapat ditemukan di sana. Sistem tipe Java memaksa opsi unpack untuk menangani ketiadaan nilai.
Tentang Penulis
Raul-Gabriel Urma adalah CEO dan salah satu pendiri Cambridge Spark (UK), komunitas pendidikan terkemuka untuk peneliti dan pengembang data. Raul terpilih sebagai salah satu peserta dalam program Java Champions pada tahun 2017. Dia telah bekerja untuk Google, eBay, Oracle, dan Goldman Sachs. Dia mempertahankan tesisnya di bidang teknik komputer di Universitas Cambridge. Selain itu, ia memegang gelar master dalam bidang teknik dari Imperial College London, lulus dengan pujian dan menerima beberapa penghargaan untuk proposal rasionalisasi. Raul telah mempresentasikan lebih dari 100 laporan teknis di konferensi internasional.
Mario Fusco adalah insinyur perangkat lunak senior di Red Hat, yang terlibat dalam pengembangan kernel Drools, mesin aturan JBoss. Dia memiliki pengalaman yang kaya dalam pengembangan Jawa, berpartisipasi (sering sebagai pengembang terkemuka) dalam banyak proyek perusahaan di berbagai industri, dari media hingga sektor keuangan. Di antara minatnya adalah pemrograman fungsional dan bahasa khusus domain. Berdasarkan dua hobi ini, ia menciptakan perpustakaan lambdaj open source, yang ingin mengembangkan Java DSL internal untuk bekerja dengan koleksi dan memungkinkan untuk menggunakan beberapa elemen pemrograman fungsional di Jawa.
Alan Mycroft adalah profesor di Departemen Ilmu Komputer di Universitas Cambridge, tempat ia mengajar sejak 1984. Dia juga seorang karyawan Robinson College, salah satu pendiri Asosiasi Bahasa dan Sistem Pemrograman Eropa, dan salah satu pendiri dan pengurus Yayasan Raspberry Pi. Ia memiliki gelar dalam bidang matematika (Cambridge) dan ilmu komputer (Edinburgh). Alan adalah penulis lebih dari 100 artikel ilmiah. Dia adalah pengawas untuk lebih dari 20 kandidat PhD. Penelitiannya terutama berkaitan dengan bidang bahasa pemrograman dan semantik, optimisasi dan implementasi. Untuk sementara waktu, ia bekerja di AT&T Laboratories dan departemen riset Intel, dan juga ikut mendirikan Codemist Ltd., yang merilis kompiler bahasa C untuk arsitektur ARM, yang disebut Norcroft.
»Informasi lebih lanjut tentang buku ini dapat ditemukan di
situs web penerbit»
Isi»
KutipanKupon diskon 25% untuk penjaja -
JawaSetelah pembayaran versi kertas buku, sebuah buku elektronik dikirim melalui email.