JavaRush /Java Blog /Random-ID /Terjemahan buku. Pemrograman fungsional di Java. Bab 1
timurnav
Level 21

Terjemahan buku. Pemrograman fungsional di Java. Bab 1

Dipublikasikan di grup Random-ID
Saya akan dengan senang hati membantu Anda menemukan kesalahan dan meningkatkan kualitas terjemahan. Saya menerjemahkan untuk meningkatkan kemampuan bahasa Inggris saya, dan jika Anda membaca dan mencari kesalahan terjemahan, maka Anda akan meningkat lebih baik dari saya. Penulis buku tersebut menulis bahwa buku tersebut mengasumsikan banyak pengalaman bekerja dengan Java, sejujurnya saya sendiri tidak terlalu berpengalaman, tetapi saya memahami materi di dalam buku tersebut. Buku ini membahas beberapa teori yang sulit dijelaskan dengan jari. Jika ada artikel yang layak di wiki, saya akan memberikan tautan ke artikel tersebut, tetapi untuk pemahaman yang lebih baik saya sarankan Anda mencarinya sendiri di Google. semoga sukses untuk semuanya. :) Bagi yang ingin mengoreksi terjemahan saya, serta bagi yang merasa terlalu buruk untuk membaca dalam bahasa Rusia, Anda dapat mendownload buku aslinya di sini . Isi Bab 1 Halo, Ekspresi Lambda - sedang membaca Bab 2 Menggunakan Koleksi - dalam pengembangan Bab 3 String, Pembanding, dan Filter - dalam pengembangan Bab 4 Pengembangan dengan Ekspresi Lambda - dalam pengembangan Bab 5 Bekerja dengan Sumber Daya - dalam pengembangan Bab 6 Menjadi Malas - dalam development Bab 7 Mengoptimalkan sumber daya - dalam pengembangan Bab 8 Tata letak dengan ekspresi lambda - dalam pengembangan Bab 9 Menyatukan semuanya - dalam pengembangan

Bab 1 Halo, Ekspresi Lambda!

Kode Java kami siap untuk transformasi luar biasa. Tugas sehari-hari yang kita lakukan menjadi lebih sederhana, mudah, dan ekspresif. Cara baru pemrograman Java telah digunakan selama beberapa dekade dalam bahasa lain. Dengan perubahan pada Java ini, kita dapat menulis kode yang ringkas, elegan, dan ekspresif dengan lebih sedikit kesalahan. Kita dapat menggunakan ini untuk menerapkan standar dengan mudah dan menerapkan pola desain umum dengan lebih sedikit baris kode. Dalam buku ini, kita mengeksplorasi gaya pemrograman fungsional menggunakan contoh-contoh langsung dari permasalahan yang kita lakukan setiap hari. Sebelum kita mendalami gaya elegan dan cara baru dalam mengembangkan perangkat lunak ini, mari kita lihat mengapa ini lebih baik.
Ubah pemikiran Anda
Gaya imperatif adalah apa yang diberikan Java kepada kita sejak awal mula bahasa tersebut. Gaya ini menyarankan agar kita menjelaskan ke Java setiap langkah dari apa yang kita ingin bahasa tersebut lakukan, dan kemudian kita memastikan bahwa langkah-langkah tersebut diikuti dengan tepat. Ini berhasil dengan baik, tetapi levelnya masih rendah. Kode tersebut akhirnya menjadi terlalu bertele-tele, dan kami sering kali menginginkan bahasa yang sedikit lebih cerdas. Kita kemudian dapat mengatakannya secara deklaratif - apa yang kita inginkan, dan bukan menyelidiki bagaimana melakukannya. Terima kasih kepada pengembangnya, Java kini dapat membantu kami melakukan hal ini. Mari kita lihat beberapa contoh untuk memahami manfaat dan perbedaan antara pendekatan-pendekatan ini.
Cara yang biasa
Mari kita mulai dengan dasar-dasar yang sudah kita kenal untuk melihat cara kerja kedua paradigma tersebut. Ini menggunakan metode penting untuk mencari Chicago di koleksi kota - daftar dalam buku ini hanya menampilkan cuplikan kode. boolean found = false; for(String city : cities) { if(city.equals("Chicago")) { found = true; break; } } System.out.println("Found chicago?:" + found); Versi imperatif dari kode ini berisik (apa hubungannya dengan kata ini?) dan tingkat rendah, ada beberapa bagian yang bisa berubah. Pertama kita membuat bendera boolean buruk yang disebut found dan kemudian kita mengulangi setiap elemen dalam koleksi. Jika kami menemukan kota yang kami cari, kami menyetel benderanya ke true dan memutus perulangan. Akhirnya kami mencetak hasil pencarian kami ke konsol.
Ada cara yang lebih baik
Sebagai pemrogram Java yang jeli, sekilas melihat kode ini dapat mengubahnya menjadi sesuatu yang lebih ekspresif dan lebih mudah dibaca, seperti ini: System.out.println("Found chicago?:" + cities.contains("Chicago")); Berikut ini contoh gaya deklaratif - metode berisi() membantu kita langsung mendapatkan apa yang kita perlukan.
Perubahan sebenarnya
Perubahan ini akan membawa sejumlah perbaikan pada kode kami:
  • Tidak perlu repot dengan variabel yang bisa berubah
  • Iterasi loop disembunyikan di bawah tenda
  • Lebih sedikit kekacauan kode
  • Kejelasan kode yang lebih baik, memusatkan perhatian
  • Lebih sedikit impedansi; kode sangat sesuai dengan maksud bisnis
  • Kemungkinan kesalahan lebih kecil
  • Lebih mudah untuk dipahami dan didukung
Selain kasus-kasus sederhana
Ini adalah contoh sederhana dari fungsi deklaratif yang memeriksa keberadaan elemen dalam koleksi; ini telah digunakan sejak lama di Java. Sekarang bayangkan tidak perlu menulis kode penting untuk operasi lebih lanjut seperti parsing file, bekerja dengan database, membuat permintaan untuk layanan web, membuat multithreading, dll. Java sekarang memungkinkan penulisan kode yang ringkas dan elegan sehingga lebih sulit membuat kesalahan, tidak hanya dalam pengoperasian sederhana, namun di seluruh aplikasi kita.
Cara lama
Mari kita lihat contoh lainnya. Kami membuat koleksi harga dan akan mencoba beberapa cara untuk menghitung jumlah semua harga diskon. Misalkan kita diminta menjumlahkan semua harga yang nilainya melebihi $20, dengan diskon 10%. Pertama mari kita lakukan ini dengan cara Java biasa. Kode ini pasti sudah familiar bagi kita: pertama-tama kita membuat variabel totalOfDiscountedPrices yang bisa diubah dimana kita akan menyimpan nilai yang dihasilkan. Kami kemudian menelusuri kumpulan harga, memilih harga di atas $20, mendapatkan harga diskon, dan menambahkan nilai tersebut ke totalOfDiscountedPrices . Pada akhirnya kami menampilkan jumlah semua harga termasuk diskon. Di bawah ini adalah keluaran ke konsol final List prices = Arrays.asList( new BigDecimal("10"), new BigDecimal("30"), new BigDecimal("17"), new BigDecimal("20"), new BigDecimal("15"), new BigDecimal("18"), new BigDecimal("45"), new BigDecimal("12")); BigDecimal totalOfDiscountedPrices = BigDecimal.ZERO; for(BigDecimal price : prices) { if(price.compareTo(BigDecimal.valueOf(20)) > 0) totalOfDiscountedPrices = totalOfDiscountedPrices.add(price.multiply(BigDecimal.valueOf(0.9))); } System.out.println("Total of discounted prices: " + totalOfDiscountedPrices);
Total harga diskon: 67,5
Ini berfungsi, tetapi kodenya terlihat berantakan. Tapi itu bukan salah kami, kami menggunakan apa yang tersedia. Kode ini tingkatnya cukup rendah - ia menderita obsesi dengan primitif (google, hal-hal menarik) dan bertentangan dengan prinsip tanggung jawab tunggal . Kita yang bekerja di rumah harus menjauhkan kode seperti itu dari pandangan anak-anak yang bercita-cita menjadi programmer, hal ini mungkin akan mengkhawatirkan pikiran mereka yang rapuh, bersiaplah untuk pertanyaan "Inikah yang harus Anda lakukan untuk bertahan hidup?"
Ada cara yang lebih baik, satu lagi
Sekarang kami bisa berbuat lebih baik, jauh lebih baik. Kode kami mungkin menyerupai persyaratan spesifikasi. Hal ini akan membantu kami mengurangi kesenjangan antara kebutuhan bisnis dan kode yang menerapkannya, sehingga mengurangi kemungkinan terjadinya salah tafsir terhadap persyaratan. Daripada membuat variabel lalu mengubahnya berulang kali, mari kita bekerja pada tingkat abstraksi yang lebih tinggi, seperti pada daftar berikut. final BigDecimal totalOfDiscountedPrices = prices.stream() .filter(price -> price.compareTo(BigDecimal.valueOf(20)) > 0) .map(price -> price.multiply(BigDecimal.valueOf(0.9))) .reduce(BigDecimal.ZERO, BigDecimal::add); System.out.println("Total of discounted prices: " + totalOfDiscountedPrices); Mari kita membacanya dengan lantang - filter harga lebih besar dari 20, petakan (buat pasangan "kunci" "nilai") menggunakan kunci "harga", harga termasuk diskon, lalu tambahkan
- komentar penerjemah berarti kata-kata yang muncul di kepala Anda saat membaca kode .filter(price -> price.compareTo(BigDecimal.valueOf(20)) > 0)
Kode dieksekusi bersama-sama dalam urutan logis yang sama seperti yang telah kita baca. Kodenya telah dipersingkat, tetapi kami menggunakan banyak hal baru dari Java 8. Pertama, kami memanggil metode stream() pada daftar harga . Ini membuka pintu ke iterator khusus dengan serangkaian fitur kenyamanan yang kaya yang akan kita bahas nanti. Daripada langsung mengulang semua nilai dalam daftar harga , kami menggunakan beberapa metode khusus seperti filter() dan map() . Berbeda dengan metode yang kami gunakan di Java dan JDK, metode ini menggunakan fungsi anonim - ekspresi lambda - sebagai parameter dalam tanda kurung. Kami akan mempelajarinya lebih detail nanti. Dengan memanggil metode pengurangan() , kami menghitung jumlah nilai (harga diskon) yang diperoleh dalam metode peta() . Perulangan disembunyikan dengan cara yang sama seperti saat menggunakan metode berisi() . Namun metode filter() dan map() bahkan lebih kompleks. Untuk setiap harga dalam daftar harga , mereka memanggil fungsi lambda yang diteruskan dan menyimpannya ke koleksi baru. Metode pengurangan() dipanggil pada koleksi ini untuk menghasilkan hasil akhir. Di bawah ini adalah keluaran ke konsol
Total harga diskon: 67,5
Perubahan
Di bawah ini adalah perubahan yang berhubungan dengan metode biasa:
  • Kodenya enak dipandang dan tidak berantakan.
  • Tidak ada operasi tingkat rendah
  • Lebih mudah untuk meningkatkan atau mengubah logika
  • Iterasi dikendalikan oleh perpustakaan metode
  • Evaluasi loop malas yang efisien
  • Lebih mudah untuk memparalelkan sesuai kebutuhan
Nanti kita akan membahas bagaimana Java menyediakan peningkatan ini.
Lambda untuk menyelamatkan :)
Lambda adalah kunci fungsional untuk membebaskan kita dari kerumitan pemrograman penting. Dengan mengubah cara kita memprogram, dengan fitur-fitur terbaru Java, kita dapat menulis kode yang tidak hanya elegan dan ringkas, tetapi juga tidak terlalu rawan kesalahan, lebih efisien, dan lebih mudah untuk dioptimalkan, ditingkatkan, dan dibuat multi-thread.
Menang Besar dari Pemrograman Fungsional
Gaya pemrograman fungsional memiliki rasio signal-to-noise yang lebih tinggi ; Kami menulis lebih sedikit baris kode, tetapi setiap baris atau ekspresi menjalankan lebih banyak fungsi. Kami memperoleh sedikit manfaat dari versi fungsional kode dibandingkan dengan versi imperatifnya:
  • Kami menghindari perubahan yang tidak diinginkan atau penetapan ulang variabel, yang merupakan sumber kesalahan dan mempersulit pemrosesan kode dari thread berbeda secara bersamaan. Dalam versi imperatif, kami menetapkan nilai berbeda untuk variabel totalOfDiscountedPrices di seluruh loop . Dalam versi fungsional, tidak ada perubahan eksplisit pada variabel dalam kode. Lebih sedikit perubahan menyebabkan lebih sedikit bug dalam kode.
  • Versi fungsional kode lebih mudah untuk diparalelkan. Meskipun perhitungan dalam metode map() panjang, kita dapat menjalankannya secara paralel tanpa rasa takut terhadap apa pun. Jika kita mengakses kode bergaya imperatif dari thread yang berbeda, kita perlu memikirkan tentang mengubah variabel totalOfDiscountedPrices secara bersamaan . Dalam versi fungsional, kita mengakses variabel hanya setelah semua perubahan dilakukan, hal ini membebaskan kita dari kekhawatiran tentang keamanan thread kode.
  • Kodenya lebih ekspresif. Daripada mengeksekusi kode dalam beberapa langkah - membuat dan menginisialisasi variabel dengan nilai dummy, mengulang daftar harga, menambahkan harga diskon ke variabel, dan seterusnya - kita cukup meminta metode map() pada daftar untuk mengembalikan daftar lainnya harga diskon dan menambahkannya .
  • Gaya fungsionalnya lebih ringkas: lebih sedikit baris kode yang diperlukan dibandingkan versi imperatif. Kode yang lebih ringkas berarti lebih sedikit penulisan, lebih sedikit pembacaan, dan lebih mudah dipelihara.
  • Versi fungsional kode ini intuitif dan mudah dipahami, setelah Anda mengetahui sintaksisnya. Metode map() menerapkan fungsi yang diteruskan (yang menghitung harga diskon) ke setiap elemen koleksi dan menghasilkan koleksi dengan hasilnya, seperti yang bisa kita lihat pada gambar di bawah.

Gambar Gambar 1 - metode peta menerapkan fungsi yang diteruskan ke setiap elemen koleksi
Dengan dukungan ekspresi lambda, kita dapat sepenuhnya memanfaatkan kekuatan gaya fungsional pemrograman di Java. Jika kita menguasai gaya ini, kita dapat membuat kode yang lebih ekspresif dan ringkas dengan lebih sedikit perubahan dan kesalahan. Sebelumnya, salah satu keunggulan utama Java adalah dukungannya terhadap paradigma berorientasi objek. Dan gaya fungsionalnya tidak bertentangan dengan OOP. Keunggulan nyata dalam beralih dari pemrograman imperatif ke deklaratif. Dengan Java 8 kita dapat menggabungkan pemrograman fungsional dengan gaya berorientasi objek dengan cukup efektif. Kita dapat terus menerapkan gaya OO pada objek, cakupannya, statusnya, dan hubungannya. Selain itu, kita dapat memodelkan perilaku dan keadaan perubahan, proses bisnis, dan pemrosesan data sebagai serangkaian rangkaian fungsi.
Mengapa kode dalam gaya fungsional?
Kita telah melihat manfaat keseluruhan dari gaya pemrograman fungsional, namun apakah gaya baru ini layak untuk dipelajari? Apakah ini akan menjadi perubahan kecil dalam bahasa atau akan mengubah hidup kita? Kita harus mendapatkan jawaban atas pertanyaan-pertanyaan ini sebelum kita membuang-buang waktu dan tenaga. Menulis kode Java tidak terlalu sulit; sintaks bahasanya sederhana. Kami merasa nyaman dengan perpustakaan dan API yang sudah dikenal. Apa yang benar-benar mengharuskan kita untuk berupaya menulis dan memelihara kode adalah aplikasi khas Perusahaan di mana kita menggunakan Java untuk pengembangan. Kita perlu memastikan bahwa sesama pemrogram menutup koneksi ke database pada waktu yang tepat, bahwa mereka tidak menahannya atau melakukan transaksi lebih lama dari yang diperlukan, bahwa mereka menangkap pengecualian sepenuhnya dan pada tingkat yang benar, bahwa mereka menerapkan dan melepaskan kunci dengan benar. ... lembaran ini bisa dilanjutkan untuk waktu yang sangat lama. Masing-masing argumen di atas saja tidak memiliki bobot, namun jika digabungkan dengan kompleksitas implementasi yang melekat, argumen tersebut menjadi sangat membebani, memakan waktu, dan sulit untuk diterapkan. Bagaimana jika kita dapat merangkum kompleksitas ini ke dalam potongan-potongan kecil kode yang juga dapat mengelolanya dengan baik? Dengan demikian, kita tidak akan terus-menerus menghabiskan energi untuk menerapkan standar. Ini akan memberikan keuntungan yang serius, jadi mari kita lihat bagaimana gaya fungsional dapat membantu.
Joe bertanya
Apakah kode pendek* berarti lebih sedikit huruf kode?
* kita berbicara tentang kata ringkas , yang mencirikan gaya fungsional kode menggunakan ekspresi lambda
Dalam konteks ini, kode etik ini dimaksudkan agar ringkas, tanpa embel-embel, dan direduksi menjadi dampak langsung agar dapat menyampaikan maksud secara lebih efektif. Ini adalah manfaat yang luas. Menulis kode itu seperti menyatukan bahan-bahan: membuatnya ringkas seperti menambahkan saus ke dalamnya. Terkadang dibutuhkan lebih banyak usaha untuk menulis kode seperti itu. Lebih sedikit kode untuk dibaca, tetapi membuat kode lebih transparan. Penting untuk menjaga kode tetap jelas saat memperpendeknya. Kode ringkas mirip dengan trik desain. Kode ini membutuhkan lebih sedikit tarian dengan rebana. Ini berarti bahwa kita dapat dengan cepat menerapkan ide-ide kita dan melanjutkan jika ide-ide tersebut berhasil, dan mengabaikannya jika ide-ide tersebut tidak memenuhi harapan.
Iterasi pada steroid
Kami menggunakan iterator untuk memproses daftar objek, serta untuk bekerja dengan Set dan Peta. Iterator yang kita gunakan di Java sudah tidak asing lagi bagi kita; meskipun primitif, namun tidak sederhana. Tidak hanya memakan banyak baris kode, tetapi juga cukup sulit untuk ditulis. Bagaimana cara kita mengulangi semua elemen koleksi? Kita bisa menggunakan perulangan for. Bagaimana cara kita memilih beberapa elemen dari koleksi? Menggunakan perulangan for yang sama, tetapi menggunakan beberapa variabel tambahan yang dapat diubah yang perlu dibandingkan dengan sesuatu dari koleksi. Lalu, setelah memilih nilai tertentu, bagaimana kita melakukan operasi pada satu nilai, misalnya nilai minimum, maksimum, atau nilai rata-rata? Sekali lagi siklus, lagi-lagi variabel baru. Hal ini mengingatkan pada pepatah, Anda tidak dapat melihat pepohonan karena hutan (aslinya menggunakan permainan kata yang berhubungan dengan iterasi dan berarti “Semuanya dilakukan, tetapi tidak semuanya berhasil” - catatan penerjemah). jdk sekarang menyediakan iterator internal untuk berbagai pernyataan: satu untuk menyederhanakan perulangan, satu untuk mengikat ketergantungan hasil yang diperlukan, satu untuk memfilter nilai keluaran, satu untuk mengembalikan nilai, dan beberapa fungsi praktis untuk mendapatkan min, maks, rata-rata, dll. Selain itu, fungsionalitas operasi ini dapat digabungkan dengan sangat mudah, sehingga kita dapat menggabungkan rangkaian operasi yang berbeda untuk mengimplementasikan logika bisnis dengan lebih mudah dan lebih sedikit kode. Ketika kita selesai, kodenya akan lebih mudah dipahami karena menciptakan solusi logis dalam urutan yang dibutuhkan oleh masalah tersebut. Kita akan melihat beberapa contoh kode tersebut di Bab 2 dan selanjutnya di buku ini.
Penerapan algoritma
Algoritma mendorong aplikasi perusahaan. Misalnya, kita perlu menyediakan operasi yang memerlukan pemeriksaan otoritas. Kami harus memastikan bahwa transaksi diselesaikan dengan cepat dan pemeriksaan diselesaikan dengan benar. Tugas-tugas tersebut seringkali direduksi menjadi metode yang sangat biasa, seperti pada daftar di bawah ini: Transaction transaction = getFromTransactionFactory(); //... Операция выполняющаяся во время транзакции... checkProgressAndCommitOrRollbackTransaction(); UpdateAuditTrail(); Ada dua masalah dengan pendekatan ini. Pertama, hal ini sering kali menyebabkan upaya pengembangan menjadi dua kali lipat, yang pada gilirannya menyebabkan peningkatan biaya pemeliharaan aplikasi. Kedua, sangat mudah untuk melewatkan pengecualian yang mungkin dimasukkan ke dalam kode aplikasi ini, sehingga membahayakan pelaksanaan transaksi dan jalannya pemeriksaan. Kita dapat menggunakan blok try-finally yang tepat, tetapi setiap kali seseorang menyentuh kode ini, kita perlu memeriksa ulang apakah logika kode tersebut tidak rusak. Jika tidak, kita bisa meninggalkan pabrik dan membalikkan seluruh kodenya. Daripada menerima transaksi, kita bisa mengirimkan kode pemrosesan ke fungsi yang dikelola dengan baik, seperti kode di bawah ini. runWithinTransaction((Transaction transaction) -> { //... Операция выполняющаяся во время транзакции... }); Perubahan kecil ini menghasilkan penghematan yang sangat besar. Algoritme untuk pemeriksaan status dan pemeriksaan aplikasi diberikan tingkat abstraksi baru dan dienkapsulasi menggunakan metode runWithinTransaction() . Dalam metode ini kami menempatkan sepotong kode yang harus dieksekusi dalam konteks transaksi. Kita tidak perlu lagi khawatir akan lupa melakukan sesuatu atau apakah kita menangkap pengecualian tersebut di tempat yang tepat. Fungsi algoritmik menangani hal ini. Masalah ini akan dibahas lebih rinci pada Bab 5.
Ekstensi Algoritma
Algoritma semakin sering digunakan, namun agar dapat digunakan sepenuhnya dalam pengembangan aplikasi perusahaan, diperlukan cara untuk memperluasnya.
Komentar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION