Javase8 yang dirilis pada 2013 akan mencakup rencana yang disebut Proyek Lambda, yang dijelaskan dalam draft JSR-335 pada bulan Juni tahun ini.
JSR-335 memperkenalkan penutupan ke Java. Penutupan ada dalam banyak bahasa populer, seperti C ++, C#. Penutupan memungkinkan kita untuk membuat penunjuk fungsi dan meneruskannya sebagai parameter. Dalam artikel ini, kita secara kasar akan melihat karakteristik Java8 dan memperkenalkan ekspresi lambda. Dan saya akan mencoba menempatkan beberapa program sampel untuk menjelaskan beberapa konsep dan tata bahasa.
Bahasa pemrograman Java memberi kita konsep antarmuka, dan metode abstrak dapat didefinisikan dalam antarmuka. Antarmuka mendefinisikan API dan berharap bahwa pengguna atau pemasok untuk mengimplementasikan metode ini. Sering kali, kami tidak membuat kelas implementasi independen untuk beberapa antarmuka.
Penggunaan anonim banyak digunakan. Adegan paling umum yang digunakan di kelas internal anonim adalah prosesor acara. Kedua, kelas internal anonim sering digunakan dalam program multi -utara.
Sama seperti yang sedang kita diskusikan, kelas anonim adalah implementasi antarmuka ninja yang diberikan. Biasanya kami meneruskan objek kelas implementasi ini sebagai parameter ke suatu metode, dan kemudian metode ini akan memanggil metode kelas implementasi ke kelas implementasi secara internal. Oleh karena itu, antarmuka ini disebut antarmuka panggilan balik.
Meskipun kategori anonim digunakan di mana -mana, mereka masih memiliki banyak masalah. Masalah utama pertama adalah kompleksitas. Kelas -kelas ini membuat level kode terlihat berantakan dan rumit, juga dikenal sebagai vertikal. Kedua, mereka tidak dapat mengakses anggota non -final dari kelas pengemasan. Kata kunci dari ini akan menjadi sangat membingungkan. Jika kelas anonim memiliki nama anggota yang sama dengan kelas pengemasannya, variabel internal akan mencakup variabel anggota eksternal. Karena kata kunci ini layak untuk objek anonim itu sendiri daripada objek enkapsulasi.
Public void anonymousexample () {string nonfinalvariable = "nonformal exmple"; Variabel Metode "; // Baris di bawah ini memberikan kesalahan kompilasi. //System.out.println ("-> "" "NonfinalVariable); System.out.println ("-> "" + Variabel); println ("->" + this.variable);}}). Outputnya adalah:
-> Jalankan metode varial-> anggota kelas runnable
Contoh ini menjelaskan masalah yang saya sebutkan di atas, dan ekspresi Lambda hampir memecahkan semua masalah yang disebabkan oleh kelas internal anonim. Sebelum kita mengeksplorasi ekspresi lambda lebih lanjut, mari kita lihat antarmuka fungsional.
Antarmuka fungsional
Antarmuka fungsional adalah antarmuka dengan hanya satu metode, yang mewakili kontrak metode ini.
Hanya satu dalam definisi di atas yang tidak begitu sederhana. Saya tidak mengerti paragraf ini.
Contoh berikut dengan jelas menunjukkan cara memahami konsep antarmuka fungsional.
antarmuka runnable {void run ();} // fungsionalInterface foo {boolean sama (objek obj);} // tidak fungsional; Fungsional memiliki satu abstrak metode non-objek forator {boolean sama (objek obj); tidak fungsional; objek. tanda tangan yang samaSebagian besar antarmuka callback adalah antarmuka fungsional. Misalnya, runnable, callable, comparator, dll. Itu sebelumnya disebut SAM (metode abstrak tunggal)
Ekspresi Lambda
Seperti yang kami katakan, masalah utama dari kategori anonim adalah bahwa tingkat kode terlihat berantakan, yaitu, privat vertikal. Ekspresi Lambda terlihat seperti metode. Mereka memiliki daftar parameter formal dan pemblokiran parameter ini.
(String s) -> s.lengh;
Contoh di atas berarti bahwa ekspresi pertama menerima variabel string sebagai parameter, dan kemudian mengembalikan panjang string. Yang kedua tanpa parameter dan mengembalikan 43. Akhirnya, yang ketiga menerima dua bilangan bulat X dan Y, dan kembali ke perdamaian.
Setelah membaca banyak kata, akhirnya, saya dapat memberikan contoh ekspresi Lambda pertama.
Kelas publik FirstLambdaExpression {variabel string publik = "variabel level kelas"; String nonfinalvariable = "Ini adalah variabel non final"; ; Outputnya adalah:
-> Metode Variabel Lokal-> Variabel Level Kelas
Anda dapat membandingkan perbedaan antara menggunakan ekspresi lambda dan kelas internal anonim. Kita dapat dengan jelas mengatakan bahwa menulis kategori anonim menggunakan ekspresi Lambda memecahkan masalah visibilitas variabel. Anda dapat melihat anotasi dalam kode.
Sintaks ekspresi lambda umum mencakup daftar parameter, kata kunci panah "->" adalah badan utama. Subjek dapat berupa ekspresi (satu pernyataan lini) atau kalimat multi -garis. Jika itu adalah ekspresi, itu akan dihitung dan dikembalikan. Break dan Lanjutkan hanya dapat digunakan di dalam siklus.
Mengapa memilih bentuk tata bahasa khusus ini, karena saat ini gaya ini biasanya dalam C# dan Scala, yang juga merupakan penulisan umum ekspresi lambda. Desain tata bahasa ini pada dasarnya memecahkan kompleksitas tipe anonim. Tetapi pada saat yang sama, ia juga sangat fleksibel. Hasil ekspresi adalah sebagai nilai pengembaliannya sendiri. Fleksibilitas ini dapat membuat kode tetap sederhana.
Ekspresi lambda digunakan sebagai anonim, sehingga dapat digunakan secara fleksibel dalam modul lain atau ekspresi lambda lainnya (ekspresi lambda bersarang).
// Lambda Exposition tertutup dengan Blok Parameter Metode.//Target Jenis Antarmuka adalah Tipe Parameter Metode. () -> {System.out.println ("Berlari di utas yang berbeda");});
Jika Anda melihat lebih dekat pada ekspresi Lambda, Anda akan melihat bahwa tipe antarmuka target bukan bagian dari ekspresi. Kompiler membantu menyimpulkan jenis dan lingkungan sekitar ekspresi lambda.
Ekspresi Lambda harus memiliki tipe target, dan mereka dapat beradaptasi dengan jenis target yang mungkin. Ketika tipe target adalah antarmuka, kondisi berikut harus dipenuhi untuk dikompilasi dengan benar:
Karena kompiler dapat mengetahui jenis dan angka parameter melalui pernyataan tipe target, dalam ekspresi lambda, deklarasi tipe parameter dapat dihilangkan.
Pembanding c = (s1, s2) -> s1.comparetoignorecase (S2);
Selain itu, jika metode yang dinyatakan dalam jenis target hanya menerima hanya satu parameter (berkali -kali ini adalah masalahnya), maka kurung kecil parameter juga dapat ditulis, misalnya:
ActionListenr listenr = event-> event.getwhen ();
Pertanyaan yang sangat jelas muncul, mengapa Lambda mengungkapkan bukan nama metode yang ditentukan?
Jawabannya adalah: ekspresi lambda hanya dapat digunakan untuk antarmuka fungsional, sedangkan antarmuka fungsional hanya memiliki satu metode.
Ketika kami menentukan antarmuka fungsional untuk membuat ekspresi lambda, kompiler dapat memahami tanda tangan antarmuka fungsional hukum Cina dan memeriksa apakah ekspresi yang diberikan cocok.
Tata bahasa yang fleksibel ini membantu kita menghindari penggunaan privem vertikal anonim, dan itu tidak akan membawa privem horizontal (sangat panjang satu kalimat).
Tata bahasa ekspresi Lambda terkait dengan konteks, tetapi ini bukan pertama kalinya. Operator berlian yang ditambahkan oleh Java SE 7 juga memiliki konsep ini, yang disimpulkan berdasarkan konteks.
void Invoke (runnable r) {r.run ()} void Future Invoke (callable r) {return c.compute ()} // Di atas adalah dua metode onceFuture s = Invoke (() -> "Done"); / Invoke mana yang akan dipanggil?
Jawaban untuk pertanyaan di atas adalah memanggil metode menerima parameter yang dapat dipanggil. Dalam hal ini, kompiler akan diselesaikan melalui beban jenis parameter yang berbeda. Ketika ada lebih dari satu metode pemuatan yang berlaku, kompiler juga memeriksa kompatibilitas ekspresi lambda dan tipe target yang sesuai. Sederhananya, metode Invoke di atas diharapkan untuk kembali, tetapi hanya satu metode Invoke yang memiliki nilai pengembalian.
Ekspresi lambda dapat secara eksplisit dikonversi menjadi tipe target yang ditentukan, selama mereka kompatibel dengan jenis yang sesuai. Melihat program berikut, saya menerapkan tiga jenis yang dapat dipanggil, dan mereka semua mengubahnya menjadi jenis kelas.
Kelas publik FirstWithLambdaExpressions {public static void main (string [] args) {listl = arrayslist (callial)-> "callable 1", (call dapat) ()-> "Callable 2", (Callable) (Callable))-> "Callable"); ) {e1.printstacktrace ();} e.shutdown ();} public void dumplist (daftar daftar) melempar InteruptException, ExecutionExcepti di {for (Future Future: List) {System.out.println (Future.get ()); }}}Seperti yang kita bahas sebelumnya, kategori anonim tidak dapat mengakses variabel non -final di lingkungan sekitarnya. Tetapi tidak ada batasan dalam ekspresi lambda.
Saat ini, antarmuka fungsional yang ditentukan hanya berlaku untuk antarmuka. Saya mencoba membuat ekspresi lambda hanya dari satu metode abstrak, tetapi kesalahan kompilasi dibuat. Menurut JSR -335, versi ekspresi Lambda di masa depan dapat mendukung kelas fungsional.
Kutipan metode
Metode dirujuk sebagai metode referensi tanpa menyebutnya.
Ekspresi Lambda memungkinkan kita untuk mendefinisikan metode anonim dan menggunakannya sebagai instance dari antarmuka fungsional. Metode sangat mirip dengan ekspresi lambda.
Sistem :: GetProperty "ABC" :: PlayString :: Spesifik :: Tostringarraylist :: New
Pernyataan di atas menunjukkan sintaks umum dari metode dan referensi ke konstruktor. Di sini kita telah melihat karakter operasi baru ":::: double colon). Saya tidak tahu nama yang tepat sebagai operator ini, tetapi JSR menyebutnya sebagai pemisah, dan halaman Wikipedia menyebutnya sebagai operasi analisis ruang lingkup lingkup lingkup Sebagai referensi, dalam kisaran tutorial ini, kami hanya akan menggunakannya sebagai separatis.
Referensi target atau penerima ditempatkan di belakang penyedia dan pemisah. Ini membentuk ekspresi yang dapat mengutip metode. Dalam pernyataan terakhir, nama metode ini adalah "baru". Ekspresi ini mengutip metode struktur kelas arraylist (referensi ke konstruktor di bagian selanjutnya)
Sebelum Anda memahami ini, saya ingin membiarkan Anda melihat kekuatan metode yang dikutip.
Impor Java.util.arrays; = {Karyawan baru ("Nick"), karyawan baru ("Robin"), karyawan baru ("Josh"), karyawan baru ("Andy"), karyawan baru ("Mark")}; Sebelum mengurutkan: "); DumpEmployee (karyawan); arrays.sort (karyawan, karyawan :: myCompare); System.out.println (" Setelah Sort: "); karyawan);] karyawan) {untuk (karyawan emp: array. aslist (karyawan)) {System.out.print (emp.name+",");} System.out.println (); (Karyawan Emp1, karyawan Emp2) {return emp1.name.compareto (emp2.name);}} Outputnya adalah:
Sebelum Sort: Nick, Robin, Josh, Andy, Mark, After Sort: Andy, Josh, Mark, Nick, Robin,
Outputnya tidak istimewa. Metode statis MyCompare menerima dua objek karyawan dan mengembalikan nama mereka untuk membandingkan.
Dalam metode utama, saya membuat array yang berbeda dari seorang karyawan, dan menyerahkannya ke metode array.
Tunggu sebentar, jika kita melihat Javadoc, Anda akan menemukan bahwa parameter kedua dari metode pengurutan adalah tipe korarator, tetapi kami melewati referensi metode statis karyawan. Masalah penting ada di sini.
Mari kita lihat alasannya. Metode arrays.sort mengharapkan instance dari pembanding, dan pembanding ini adalah antarmuka fungsional, yang berarti bahwa ia hanya memiliki satu metode, yaitu, perbandingan. Di sini kami juga dengan jahat meneruskan ekspresi Lambda, yang menyediakan implementasi metode Compaare dalam ungkapan ini. Namun di dalam kami, kelas karyawan kami sudah memiliki metode perbandingan. Hanya saja nama mereka berbeda.
Ketika ada beberapa metode dengan nama yang sama, kompiler akan memilih pencocokan terbaik sesuai dengan jenis target. Untuk memahami, lihatlah sebuah contoh:
MyCompare statis public (karyawan Emp1, karyawan Emp2) {return emp1.name.compareto (emp2.name);} // Metode lain dengan nama yang sama dengan. {{) {{) {{Return int1.compareto (int2);} Saya membuat dua array berbeda untuk disortir.
Karyawan [] karyawan = {karyawan baru ("nick"), karyawan baru ("robin"), karyawan baru ("josh"), karyawan baru ("Andy"), karyawan baru ("Mark")}; INS = {1, 4, 8, 2, 3, 8, 6}; Sekarang, saya menjalankan dua baris kode berikut
Arrays.sort (karyawan, karyawan :: myCompare);
Di sini, metode referensi dalam dua baris kode adalah sama (karyawan :: myCompare).
Jangan disesatkan oleh metode statis, kita juga dapat membuat referensi ke metode contoh. Untuk metode statis, kami menggunakan nama kelas :: Metode nama untuk menulis referensi metode.
Contoh di atas cukup baik, tetapi kami tidak perlu menulis metode untuk perbandingan integer, karena Integer telah menerapkan yang sebanding dan menyediakan metode implementasi Compareto. Jadi kami hanya menggunakan baris berikut secara langsung:
Arrays.sort (ints, integer :: compareto);
Melihat ini, apakah Anda merasa sedikit bingung? TIDAK? Kemudian saya bingung dengan Anda di sini. Metode anggota dirujuk :: harus menjadi objek sebelumnya, tetapi mengapa kalimat di sini memang sah.
Jawabannya adalah: Jenis pernyataan ini memungkinkan penggunaan dalam beberapa jenis tertentu. Integer adalah tipe data, dan untuk tipe data, pernyataan ini diperbolehkan.
Jika kita mengubah metode karyawan saya menjadi non -statis, dan kemudian gunakan: karyawan :: myCompare, akan ada kesalahan kompilasi: tidak ada metode yang cocok ditemukan.
Referensi metode konstruktif
Referensi konstruktor digunakan sebagai kelas yang merujuk konstruktor tanpa institusionalisasi yang ditentukan.
Referensi metode konstruktif adalah fitur baru Javase 8. Kami dapat membuat referensi ke metode konstruktif dan meneruskannya sebagai parameter ke tipe target.
Ketika kami menggunakannya untuk merujuk, kami mengutip satu metode yang ada untuk menggunakannya. Demikian pula, saat menggunakan referensi metode konstruktif, kami membuat referensi untuk metode konstruktif yang ada.
Pada bagian sebelumnya, kita telah melihat nama tata bahasa yang dirujuk oleh konstruktor :: baru, yang terlihat seperti referensi metode. Referensi untuk metode yang dibangun ini dapat ditetapkan untuk contoh antarmuka fungsional target. Mungkin ada beberapa konstruktor di kelas.
Bagi saya, sulit untuk menulis metode konstruktif pertama. Pada akhirnya, saya menghabiskan waktu lama bekerja keras, dan akhirnya "Ah, saya menemukan ...", lihat prosedur berikut.
Public ClassReferences {public static void main (string [] ar) {myinterface in = myclass :: new; System.out.println ());}} antarmuka myinterface {myclass getMeMyObject ()} kelas myclass {myclass () {} } Outputnya adalah:
-> com.myclass@34e5307e
Ini terlihat agak luar biasa, bukan?
Contoh ini membangkitkan masalah lain di hati saya: bagaimana cara instantiate metode konstruktif dengan parameter? Lihatlah prosedur di bawah ini:
Public Construcorreferences {public static void main (string [] ar) {emlpoyeproder penyedia = karyawan :: baru; ; usia) {this.name = name; Outputnya adalah:
-> Nama Karyawan: John-> Usia Karyawan: 30
Sebelum membaca artikel ini, mari kita lihat fitur paling keren dalam metode javase8-default
Metode default
Javase8 akan memperkenalkan konsep yang disebut metode default. Versi Java awal dari antarmuka memiliki antarmuka yang sangat ketat. Dalam versi Java yang akan datang, implementasi default dari metode di antarmuka diizinkan. Tidak banyak omong kosong, lihat yang berikut:
Public DefaultMethods {public static void main (string [] ar) {normalinterface instance = new NormalInterfaceImpl (); System.out.println ("-> myDefaultMethod");}} kelas NormalInfaceImpl ImplemeLeMerInterface {@Override public void mynormalmethod () {) {)}} Outputnya adalah:
-> MyDefaultMethod
Antarmuka di atas menyatakan dua metode, tetapi kelas implementasi antarmuka ini hanya mewujudkan salah satunya, karena MyDefaultMethod menggunakan pengubah default, dan menyediakan blok metode untuk implementasi default. Aturan beban berat GM masih berlaku di sini. Jika kelas implementasi mengimplementasikan metode dalam antarmuka, itu akan menjadi metode di kelas panggilan saat menelepon, jika tidak, implementasi default akan dipanggil.
Antarmuka antarmuka induk terintegrasi dapat meningkatkan, mengubah, dan menghapus implementasi default antarmuka induk.
Antarmuka ParentInterface {void awalnyaNomal (); -> awalnya nomal ");} batal awalnya default (); // Sekarang metode normal} Dalam contoh ini, ParentInterface mendefinisikan dua metode, satu normal, dan yang lainnya diimplementasikan secara default.
Bayangkan sebuah kelas mewarisi Kelas C, menyadari antarmuka I, dan C memiliki metode, dan metode yang memberikan metode default di mana asalkan metode default kompatibel. Dalam hal ini, metode dalam C akan memberikan prioritas pada metode default di I, dan bahkan metode dalam C masih menjadi prioritas ketika metode ini abstrak.
Public DefaultMethods {public static void main (string [] ar) {interfaxe IMP = new NormalInterfaceImpl (); ;}} antarmuka antarmuka {public void defaultMethod () default {System.out.println ("-> Interfaxe"); Outputnya adalah:
-> ParentClass
Contoh kedua adalah bahwa kelas saya telah menerapkan dua antarmuka yang berbeda, tetapi kedua antarmuka memberikan pernyataan yang sama dengan metode implementasi default yang sama. Dalam hal ini, kompiler tidak akan dapat mengetahui apa yang sedang terjadi. Ini dapat dilakukan dengan cara berikut.
DefaultMethods Public {public static void main (string [] ar) {firstInterface IMP = new NormalInterFampl (); )}} antarmuka SecondInterface {public void defaultMethod () default {System.out.println ("-> SecondInterface"); Outputnya adalah:
-> SecondInterface
Sekarang, kami telah membaca pengenalan penutupan Java. Dalam artikel ini, kami bersentuhan dengan antarmuka fungsional dan penutupan Java, yang memahami ekspresi Lambda Java, referensi metode, dan referensi konstruktor. Dan kami juga menulis contoh hello dunia dari ekspresi lambda.
Javase8 akan segera hadir.