ringkasan
Ketika saya bebas, saya akan meluangkan waktu untuk belajar Groovy dan Scala berjalan di JVM, dan menemukan bahwa mereka menangani nol jauh lebih hati -hati daripada versi Java sebelumnya. Di Java 8, opsional memberikan solusi yang sangat elegan untuk pemrograman fungsional pemrosesan nol. Artikel ini akan menjelaskan penanganan nol yang sudah lama ada di Java, dan kemudian memperkenalkan penggunaan opsional untuk mengimplementasikan pemrograman fungsional Java.
Nol yang mengganggu kami di tahun -tahun itu
Ada legenda di dunia Java: hanya ketika Anda benar -benar memahami pengecualian pointer nol, Anda dapat dianggap sebagai pengembang Java yang memenuhi syarat. Dalam karir karakter Java kami, kami akan menemukan berbagai pemrosesan nol setiap hari. Kami dapat menulis kode seperti berikut berulang kali setiap hari:
if (null! = obj1) {if (null! = obje2) {// lakukan sesuatu}}Jika Anda memiliki sedikit visi, Javaer akan melakukan beberapa hal cantik dan menemukan cara untuk menilai NULL:
boolean checknotnull (objek obj) {return null == obj? Salah: Benar; } void do () {if (checknotnull (obj1)) {if (checknotnull (obj2)) {// lakukan sesuatu}}}Lalu, pertanyaannya muncul lagi: jika nol mewakili string kosong, apa artinya ""?
Kemudian pemikiran inersia memberi tahu kita, "" dan nol keduanya kode string kosong? Saya cukup meningkatkan nilai nol:
boolean checknotblank (objek obj) {return null! = obj &&! "". Equals (obj)? Benar: false; } void do () {if (checknotblank (obj1)) {if (checkNotnull (obj2)) {// lakukan sesuatu}}}Jika Anda punya waktu, Anda dapat memeriksa berapa banyak kode yang telah Anda tulis dalam proyek saat ini atau kode masa lalu Anda dan apa yang mirip dengan kode di atas.
Saya bertanya -tanya apakah Anda benar -benar memikirkan pertanyaan: Apa arti nol?
Mengingat, berapa kali kita bertemu Java.lang.nullpointerexception pengecualian dalam karier gram sebelumnya? NullpointerException adalah pengecualian tingkat runimeException yang tidak perlu ditangkap. Jika kita secara tidak sengaja menanganinya, kita sering melihat berbagai output tumpukan pengecualian yang disebabkan oleh nullpointerexception dalam log produksi. Dan berdasarkan informasi tumpukan pengecualian ini, kami tidak dapat menemukan penyebab masalah sama sekali, karena itu bukan tempat di mana NullpointerException dilemparkan, yang menyebabkan masalah. Kita harus pergi lebih dalam untuk mencari tahu di mana nol ini dihasilkan, dan log seringkali tidak dapat dilacak saat ini.
Kadang -kadang bahkan lebih tragis bahwa tempat -tempat di mana nilai -nilai nol diproduksi seringkali tidak ada dalam kode proyek kita sendiri. Ini memiliki fakta yang lebih memalukan - ketika kita memanggil berbagai antarmuka pihak ketiga dengan kualitas yang berbeda, sulit untuk mengetahui apakah suatu antarmuka mengembalikan nol secara kebetulan ...
Kembali ke masalah kognitif sebelumnya nol. Banyak Javaers percaya bahwa null berarti "tidak ada" atau "nilai tidak ada". Menurut pemikiran inersia ini, logika kode kami adalah: Anda memanggil antarmuka saya dan mengembalikan "nilai" yang sesuai sesuai dengan parameter yang Anda berikan kepada saya. Jika kondisi ini tidak dapat menemukan "nilai" yang sesuai, maka tentu saja saya akan mengembalikan nol kepada Anda bahwa tidak ada "hal". Mari kita lihat kode berikut, yang ditulis dengan gaya pengkodean Java yang sangat tradisional dan standar:
kelas myEntity {int id; Nama string; String getName () {return name; }} // MainPublic Class Test {public static void main (string [] args) final myentity myEntity = getMyEntity (false); System.out.println (myEntity.getName ()); } private getMyEntity (boolean issuc) {if (issuc) {return new myEntity (); } else {return null; }}}Kode ini sangat sederhana, dan kode bisnis harian jelas jauh lebih rumit dari ini, tetapi pada kenyataannya, sejumlah besar kode Java kami ditulis sesuai dengan rutinitas ini. Orang yang tahu cara menggunakan barang dapat melihat sekilas bahwa mereka pasti akan melempar NullpointerException pada akhirnya. Namun, ketika kami menulis kode bisnis, kami jarang berpikir untuk berurusan dengan nol yang mungkin ini (mungkin dokumen API telah ditulis dengan sangat jelas dan akan mengembalikan nol dalam beberapa kasus, tetapi apakah Anda memastikan Anda akan dengan cermat membaca dokumen API sebelum mulai menulis kode?), Sampai kami mencapai tahap tertentu, yang akan ditambahkan ke dalam solus yang akan ditambahkan ke dalam solus yang tiba -tiba muncul dengan solid.
// tes kelas MainPublic {public static void main (string [] args) final myentity myEntity = getMyEntity (false); if (null! = myEntity) {System.out.println (myEntity.getName ()); } else {System.out.println ("error"); }}}Pikirkan baik -baik tentang beberapa tahun terakhir, apakah kita semua melakukan ini? Jika beberapa masalah nol tidak dapat ditemukan sampai fase pengujian, maka masalahnya sekarang - berapa banyak nol yang tidak diproses dengan baik dalam kode bisnis yang kompleks dan terstruktur dengan baik?
Kematangan dan kekakuan suatu proyek sering kali dapat dilihat ketika berhadapan dengan nol. Misalnya, Guava memberikan metode pemrosesan nol yang elegan sebelum JDK1.6, yang menunjukkan seberapa dalam keterampilannya.
Nol hantu mencegah kita dari maju
Jika Anda seorang Javaer yang berfokus pada pengembangan berorientasi objek tradisional, Anda dapat terbiasa dengan berbagai masalah yang disebabkan oleh nol. Tetapi bertahun -tahun yang lalu, Dewa Besar mengatakan bahwa nol adalah lubang.
Tony Hall (tidakkah Anda tahu siapa orang ini? Periksa sendiri) pernah berkata: "Saya menyebutnya kesalahan miliaran dolar saya. Itu adalah penemuan referensi nol pada tahun 1965. Saya tidak bisa menahan godaan untuk memasukkan referensi nol, hanya karena sangat mudah untuk diimplementasikan." (Makna umum adalah: "Saya menyebutnya penemuan null kesalahan yang tak ternilai. Karena di hutan belantara komputer pada tahun 1965, referensi kosong terlalu mudah diimplementasikan, jadi saya tidak bisa menahan godaan untuk menciptakan penunjuk nol.").
Kemudian, mari kita lihat masalah apa yang akan diperkenalkan nol.
Lihat kode berikut:
Alamat String = person.getCountry (). GetProvince (). GetCity ();
Jika Anda telah memainkan beberapa bahasa fungsional (Haskell, Erlang, Clojure, Scala, dll.), Yang di atas adalah cara menulis yang sangat alami. Tentu saja, metode penulisan di atas dapat diimplementasikan menggunakan Java.
Tetapi untuk menangani semua pengecualian nol yang mungkin dengan sempurna, kita harus mengubah paradigma pemrograman fungsi yang elegan ini:
if (orang! = null) {country country = person.getCountry (); if (country! = null) {province province = country.getProvince (); if (province! = null) {address = province.getCity (); }}}Dalam sekejap, pemrograman fungsional berkualitas tinggi Java8 kembali ke 10 tahun yang lalu. Penilaian bersarang seperti itu masih sepele, meningkatkan jumlah kode dan menjadi tidak masuk akal. Lebih mungkin terjadi: untuk sebagian besar waktu, orang akan lupa untuk menilai nol yang mungkin terjadi, bahkan orang tua yang memiliki kode tertulis selama bertahun -tahun tidak terkecuali.
Bagian di atas dari pemrosesan nol, yang merupakan lapisan demi lapisan bersarang, juga merupakan bagian dari Java tradisional yang dikritik untuk waktu yang lama. Jika Anda menggunakan versi Java awal sebagai bahasa pencerahan Anda, bau get-> jika null-> kembali akan memengaruhi Anda untuk waktu yang lama (ingat dalam komunitas asing, yang disebut: pengembangan berorientasi entitas).
Menggunakan Opsional untuk Mengimplementasikan Pemrograman Fungsional Java
Oke, setelah berbicara tentang semua jenis masalah, kita bisa memasuki era baru.
Jauh sebelum peluncuran versi Java SE 8, bahasa pengembangan fungsional serupa lainnya memiliki berbagai solusi mereka sendiri. Ini kode Groovy:
Versi string = komputer? .GetSoundCard () ?. getUsb () ?. getVersion (): "unkonwn";
Haskell menggunakan pengidentifikasi kelas tipe yang mungkin untuk memproses nilai nol. Scala, yang dikenal sebagai bahasa pengembangan multi-paradigma, memberikan opsi [t] yang mirip dengan mungkin, untuk membungkus dan memproses nol.
Java8 memperkenalkan java.util.Optional <T> untuk menangani masalah nol pemrograman fungsional. Gagasan pemrosesan <t> opsional mirip dengan Haskell dan Scala, tetapi ada beberapa perbedaan. Mari kita lihat contoh kode java berikut:
tes kelas publik {public static void main (string [] args) {final string text = "Hallo World!"; Opsional.ofnullable (teks) // Tampilkan untuk membuat shell.map opsional (test :: print) .map (test :: print) .ifpresent (System.out :: println); Opsional.ofnullable (teks) .map (s -> {System.out.println (s); return s.substring (6);}) .map (s -> null) // return null .ifpresent (System.out :: println); } // Cetak dan intersepkan string setelah str [5] private static string print (string str) {System.out.println (str); return str.substring (6); }} // output konsol // num1: Hallo World! // num2: world! // num3: // num4: hallo world!(Anda dapat menyalin kode di atas ke IDE Anda, asalkan JDK8 harus diinstal.)
Dua opsional dibuat dalam kode di atas, dan fungsi yang diterapkan pada dasarnya sama. Mereka berdua menggunakan opsional sebagai string shell untuk memotong string. Ketika nilai nol ditemui selama pemrosesan, pemrosesan tidak akan lagi dilanjutkan. Kita dapat menemukan bahwa setelah S-> null muncul di opsional kedua, ifpresent berikutnya tidak akan lagi dieksekusi.
Perhatikan output // num3:, yang berarti bahwa karakter "" adalah output, bukan nol.
Opsional menyediakan antarmuka yang kaya untuk menangani berbagai situasi, seperti memodifikasi kode menjadi:
tes kelas publik {public static void main (string [] args) {final string text = "Hallo World!"; System.out.println (huruf kecil (teks)); // Metode satu huruf kecil (null, system.out :: println); // Metode dua} private static string huruf kecil (string str) {return opsional.ofnullable (str) .map (s -> s.tolowercase ()). Peta (s-> s.replace ("s." nol (") ("). } private static void huruf kecil (string str string, konsumen <string> konsumen) {consumer.accept (huruf kecil (str)); }} // output // hallo java! // nanDengan cara ini, kita dapat memproses string secara dinamis. Jika nilainya ditemukan nol kapan saja, kami menggunakan Orelse untuk mengembalikan default preset "nan".
Secara umum, kita dapat membungkus struktur data apa pun secara opsional dan kemudian memprosesnya secara fungsional tanpa harus khawatir tentang nol yang mungkin muncul kapan saja.
Mari kita lihat bagaimana orang.
Metode pertama adalah tidak mengubah entitas sebelumnya:
impor java.util.optional; tes kelas publik {public static void main (string [] args) {System.out.println (opsional.ofnullable (orang baru ()) .map (x-> x.country) .map (x-> x.provinec) .map (x-> x.city). .orelse ("unkonwn")); }} class person {country country;} class country {province provisionec;} class province {city city;} class city {string name;}Di sini, opsional digunakan saat cangkang kembali setiap saat. Jika posisi tertentu kembali nol, Anda akan mendapatkan "unkonwn".
Metode kedua adalah mendefinisikan semua nilai dalam opsional:
Impor java.util.optional; tes kelas publik {public static void main (string [] args) {System.out.println (orang baru () .country.flatmap (x -> x.provinec) .flatMap (provinsi :: getCity) .flatMap (x -x.name). atau atau getCity (getCity) .FlatMap (x -> x.name). }} class Person {Opsional <Country> country = opsional.empty ();} class country {opsional <province> provisionc;} class province {opsional <ity> City; Opsional <ity> getCity () {// untuk :: return city; }} class city {opsional <string> name;}Metode pertama dapat diintegrasikan dengan lancar dengan JavaBeans, entitas atau POJA yang ada tanpa perubahan, dan dapat lebih mudah diintegrasikan ke dalam antarmuka pihak ketiga (seperti kacang pegas). Dianjurkan agar metode opsional pertama digunakan sebagai metode utama. Lagi pula, tidak semua orang di tim dapat memahami tujuan setiap get/set dengan opsional.
Opsional juga menyediakan metode filter untuk memfilter data (pada kenyataannya, antarmuka aliran-gaya di Java 8 menyediakan metode filter). Misalnya, di masa lalu kami menilai bahwa nilai ada dan membuat pemrosesan yang sesuai:
if (province! = null) {city city = province.getCity (); if (null! = city && "guangzhou" .equals (city.getName ()) {System.out.println (city.getName ());} else {System.out.println ("unkonwn");}}Sekarang kita bisa memodifikasinya
Opsional.ofnullable (provinsi) .map (x-> x.city) .filter (x-> "Guangzhou" .Equals (x.getname ())) .map (x-> x.name) .orelse ("unkonw");Pada titik ini, pengantar pemrograman fungsional selesai menggunakan opsional. Selain metode yang disebutkan di atas, opsional juga menyediakan metode berdasarkan lebih banyak kebutuhan, orelseget, orelsethrow, dll. Orelseget akan melempar pengecualian pointer nol karena nilai nol, dan orelsethrow akan melempar pengecualian yang ditentukan pengguna saat nol terjadi. Anda dapat melihat dokumentasi API untuk mempelajari lebih lanjut tentang semua metode.
Ditulis di akhir
Opsional hanyalah puncak gunung es dari pemrograman fungsional Java. Penting untuk menggabungkan fitur -fitur seperti Lambda, stream, dan FunctionInterface untuk benar -benar memahami efektivitas pemrograman fungsional Java8. Saya awalnya ingin memperkenalkan beberapa kode sumber opsional dan prinsip operasi, tetapi opsional itu sendiri memiliki kode yang sangat sedikit dan tidak banyak antarmuka API. Jika Anda memikirkannya dengan cermat, tidak ada yang bisa dikatakan dan Anda akan menghilangkannya.
Meskipun opsional elegan, saya pribadi merasa bahwa ada beberapa masalah efisiensi, tetapi belum diverifikasi. Jika ada yang punya data, beri tahu saya.
Saya bukan "pendukung pemrograman fungsional". Dari perspektif manajer tim, untuk setiap kesulitan belajar sedikit meningkat, biaya penggunaan personel dan interaksi tim akan lebih tinggi. Sama seperti di legenda, LISP dapat memiliki kode tiga puluh kali lebih sedikit daripada C ++ dan lebih efisien dalam pengembangan, tetapi jika perusahaan IT konvensional domestik benar -benar menggunakan LISP untuk melakukan proyek, ke mana harus pergi dan berapa biaya untuk mendapatkan orang -orang ini yang menggunakan LISP?
Tapi saya sangat mendorong semua orang untuk belajar dan memahami ide -ide pemrograman fungsional. Terutama pengembang yang dulunya diserang oleh Java dan masih tidak tahu perubahan apa yang akan dibawa Java8, Java8 adalah peluang yang bagus. Juga didorong untuk memperkenalkan fitur Java8 baru ke dalam proyek saat ini. Sebuah tim yang bekerja sama untuk waktu yang lama dan bahasa pemrograman kuno perlu terus -menerus menyuntikkan vitalitas baru, jika tidak, Anda akan mundur jika Anda tidak maju.
Di atas adalah kumpulan informasi opsional Java. Kami akan terus menambahkan informasi yang relevan di masa mendatang. Terima kasih atas dukungan Anda untuk situs web ini!