Mengapa menggunakan ekspresi lambda
Mari kita lihat beberapa contoh:
Contoh pertama adalah menjalankan tugas di utas terpisah, yang biasanya kami terapkan sebagai berikut:
Class Worker mengimplementasikan runnable {public void run () {for (int i = 0; i <100; i ++) dowork (); } ...} pekerja w = pekerja baru (); utas baru (w) .start ();Contoh kedua adalah metode perbandingan string khusus (dengan panjang string), yang umumnya dilakukan:
Class lengthComparator mengimplementasikan pembanding <string> {public int compare (string first, string kedua) {return integer.compare (first.length (), second.length ()); }} Arrays.sort (string, baru lengthComparator ());Dalam contoh ketiga, di JavaFX, tambahkan panggilan balik ke tombol:
Button.setonAction (EventHandler baru <ActionEvent> () {public void handle (ActionEvent Event) {System.out.println ("Terima kasih telah mengklik!");}});Contoh -contoh ini memiliki satu kesamaan, yaitu mereka pertama kali mendefinisikan blok kode, meneruskannya ke objek atau metode, dan kemudian menjalankannya. Sebelum ekspresi Lambda, Java tidak mengizinkan pemblokiran blok kode langsung, karena Java berorientasi objek, sehingga objek harus diteruskan untuk merangkum blok kode untuk dieksekusi ke dalam objek.
Sintaks ekspresi lambda
Panjang komparator dalam contoh kedua di atas dinyatakan sebagai ekspresi lambda:
(String First, String Second) -> Integer.Compare (first.length (), Second.Length ());
-> Sebelumnya adalah daftar parameter, diikuti oleh badan pernyataan ekspresi;
Jika tubuh pernyataan ekspresi lebih dari satu baris, tubuh pernyataan ditulis dalam {}, seperti fungsi biasa:
(String First, String Second) -> {if (first.length ()> Second.length ()) {return 1; } else if (first.length () == detik.length ()) {return 0; } else {return -1; }};Jika tidak ada parameter, () masih perlu dibawa bersama Anda. Misalnya, contoh pertama di atas dapat dinyatakan sebagai:
() -> {for (int i = 0; i <1000; i ++) {dowork (); }}Jika jenis parameter dapat secara otomatis disimpulkan dari konteks, Anda dapat menghilangkan:
Komparator <string> comp = (pertama, kedua) // Sama seperti (string pertama, string kedua) -> integer.compare (first.length (), detik.length ());
Jika hanya ada satu parameter dan jenisnya dapat disimpulkan secara otomatis, kurung () juga dapat dihilangkan:
// Alih -alih (Event) -> atau (ActionEvent Event) -> EventHandler <ActionEvent> listener = Event -> System.out.println ("Terima kasih telah mengklik!");Jenis nilai pengembalian ekspresi lambda secara otomatis disimpulkan, sehingga tidak perlu ditentukan; Dalam ekspresi lambda, beberapa cabang bersyarat memiliki nilai pengembalian, tetapi cabang lain tidak memiliki nilai pengembalian, yang tidak diperbolehkan, seperti:
(x) -> {if (x> = 0) {return 1; }}Selain itu, perbedaan antara ekspresi lambda dan pernyataan lambda adalah bahwa ekspresi lambda tidak perlu menulis kata kunci pengembalian. Java Runtime akan mengembalikan hasil ekspresi sebagai nilai pengembalian, sedangkan pernyataan Lambda adalah ekspresi yang ditulis dalam {}, dan kata kunci pengembalian perlu digunakan, misalnya:
// ekspresi lambdacomparator <string> comp1 = (pertama, kedua) -> integer.compare (first.length (), kedua.length ()); // pernyataan lambdacomparator <string> comp2 = (first, second) -> {return integer.compare (first.length (), kedua,}); Antarmuka fungsional
Jika suatu antarmuka hanya memiliki satu metode abstrak, itu disebut
Antarmuka fungsional, seperti runnable, pembanding, dll.
Di mana pun di mana objek antarmuka fungsional diperlukan, Anda dapat menggunakan ekspresi lambda:
Arrays.sort (Words, (First, Second) -> integer.compare (first.length (), Second.length ()));
Di sini, parameter sort () kedua membutuhkan objek pembanding, dan komparator adalah
Antarmuka fungsional, sehingga Anda dapat langsung melewati ekspresi lambda. Saat memanggil metode perbandingan () objek, itu adalah untuk menjalankan badan pernyataan dalam ekspresi lambda;
Jika pernyataan ekspresi lambda melempar pengecualian, metode abstrak yang sesuai dalam antarmuka fungsional harus melempar pengecualian, jika tidak perlu secara eksplisit menangkap pengecualian dalam ekspresi lambda:
Runnable r = ()-> {System.out.println ("-------"); coba {thread.sleep (10); } catch (InterruptedException e) {// Catch Exception}}; Callable <String> c = ()-> {System.out.println ("----------"); Thread.sleep (10); kembali "";}; Referensi metode
Jika parameter ekspresi lambda dilewatkan sebagai parameter ke metode dan efek eksekusi sama, ekspresi lambda dapat diekspresikan menggunakan referensi metode, dan dua metode berikut adalah setara:
(x) -> System.out.println (x) System.out :: println
Di antara mereka, System.out :: println disebut referensi metode.
Referensi metode terutama datang dalam tiga bentuk:
Untuk dua metode pertama, parameter ekspresi Lambda yang sesuai dan parameter metode adalah sama, seperti:
System.out :: println (x) -> System.out.println (x) Math :: pow (x, y) -> math.pow (x, y)
Untuk metode ketiga, dalam badan pernyataan ekspresi Lambda yang sesuai, parameter pertama digunakan sebagai objek, metode ini disebut, dan parameter lain digunakan sebagai parameter metode, seperti:
String :: comparetoignorecase (S1, S2) -> S1.Comparetoignorecase (S2) 1.5 Referensi Konstruktor
Referensi konstruktor mirip dengan referensi metode, tetapi merupakan metode khusus: baru. Konstruktor spesifik ditentukan oleh lingkungan konteks, seperti:
Daftar <string> label = ...; stream <utute> stream = label.stream (). Map (tombol :: baru);
Tombol :: Baru setara dengan (x) -> tombol (x), sehingga konstruktor dipanggil adalah: tombol (x);
Selain membuat objek tunggal, Anda juga dapat membuat array objek, seperti dua setara berikut:
int [] :: baru (x) -> int baru [x]
Ruang lingkup variabel
Lambd Expressions menangkap variabel yang tersedia dalam ruang lingkup saat ini, seperti:
public void repeatMessage (string text, int count) {runnable r = () -> {for (int i = 0; i <count; i ++) {System.out.println (teks); Thread.yield (); }}; utas baru (r) .start ();}Tetapi variabel -variabel ini harus tidak berubah, mengapa? Lihat contoh berikut:
int mencocokkan = 0; untuk (path p: file) utas baru (() -> {if (p memiliki beberapa properti) cocok ++;}). start (); // ilegal untuk bermutasi kecocokanKarena variabel yang dapat berubah tidak aman-utas dalam ekspresi lambda, ini konsisten dengan persyaratan kelas dalam, dan hanya variabel akhir yang didefinisikan secara eksternal yang dapat dirujuk dalam kelas dalam;
Ruang lingkup ekspresi lambda sama dengan blok kode bersarang, sehingga nama parameter atau nama variabel dalam ekspresi lambd tidak dapat bertentangan dengan variabel lokal, seperti:
Path first = paths.get ("/usr/bin"); comparator <string> comp = (first, second) -> integer.compare (first.length (), Second.length ()); // kesalahan: variabel pertama sudah ditentukanJika variabel ini dirujuk dalam ekspresi lambda, referensi ini adalah variabel metode ini yang menciptakan ekspresi lambda, seperti:
aplikasi kelas publik () {public void dowork () {runnable runner = () -> {...; System.out.println (this.toString ()); ...}; }} Jadi di sini this.tostring () memanggil tostring () dari objek aplikasi, tidak runnable
objek.
Metode default
Hanya ada metode abstrak di antarmuka. Jika metode baru ditambahkan ke antarmuka yang ada, semua kelas implementasi antarmuka perlu mengimplementasikan metode ini.
Java 8 memperkenalkan konsep metode default, dan menambahkan metode default ke antarmuka, yang tidak akan menghancurkan aturan antarmuka yang ada. Kelas implementasi antarmuka dapat memilih untuk mengganti atau secara langsung mewarisi metode default, seperti:
antarmuka orang {long getId (); string default getName () {return "John Q. public"; }}Java memungkinkan banyak warisan. Bagaimana cara menangani konflik ini jika metode yang ditentukan dalam kelas induk dari suatu kelas persis sama dengan metode default yang ditentukan dalam antarmuka, atau dua antarmuka kelas persis sama, bagaimana menangani konflik ini? Aturan pemrosesan adalah sebagai berikut:
Jika metode konflik antara kelas induk dan antarmuka: metode dalam kelas induk akan berlaku, dan metode dalam antarmuka harus diabaikan;
Jika metode default dalam dua konflik antarmuka, Anda perlu mengesampingkan metode untuk menyelesaikan konflik;
Metode statis
Sebelum Java 8, hanya variabel statis yang dapat didefinisikan dalam antarmuka. Mulai dari Java 8, metode statis dapat ditambahkan ke antarmuka, seperti
Antarmuka pembanding telah menambahkan serangkaian metode statis membandingkanxxx, seperti:
public static <T> Komparator <T> MembandingkanInt (TOintFunction <? Super T> keyextractor) {Objects.Requirenonnull (KeyExtractor); return (pembanding <T> & serializable) (C1, C2) -> integer.compare (keyextractor.Applyasint (C1), keyextractor.Applyasint (C2));}Menggunakan metode statis ini, dua metode berikut juga setara:
1.
Arrays.sort (kota, (pertama, kedua) -> integer.compare (first.length (), kedua.length ()));
2.
Arrays.sort (kota, pembanding.
Oleh karena itu, ketika kita merancang antarmuka kita sendiri di masa depan, kita tidak perlu lagi mendefinisikan kelas alat yang terpisah (seperti koleksi/koleksi).
Cukup gunakan metode statis di antarmuka.
Kelas internal anonim
Di dunia Java, kelas dalam anonim dapat mengimplementasikan operasi yang hanya dapat dilakukan sekali dalam suatu aplikasi. Misalnya, dalam aplikasi Android, acara klik tombol ditangani. Anda tidak perlu menulis kelas terpisah untuk menangani acara klik, Anda dapat melakukan ini dengan kelas dalam anonim:
Tombol tombol = (tombol) findViewById (r.id.button1); tombol.setOnclickListener (OnClickListener baru () {@Override public void OnClick (Lihat tampilan) {toast.maketext (MainActivity.ini, "Tombol Klik", Toast.Length_short) .show (); Lambda Contoh 1. Runnable Lambda Mari kita lihat beberapa contoh. Berikut adalah contoh runnable: public void runnableTest () {System.out.println ("=== runnableTest ==="); // anonim runnable runnable r1 = runnable baru () {@Override public void run () {System.out.println ("Hello World One!"); }}; // lambda runnable runnable r2 = () -> System.out.println ("Hello World Two!"); // Jalankan dua fungsi lari r1.run (); r2.run (); } public void runnableTest () {System.out.println ("=== runnableTest ==="); // anonim runnable runnable r1 = runnable baru () {@Override public void run () {System.out.println ("Hello World One!"); }}; // lambda runnable runnable r2 = () -> System.out.println ("Hello World Two!"); // Jalankan dua fungsi lari r1.run (); r2.run (); } Baik implementasi maupun nilai pengembalian tidak dikembalikan. Runnable Lambda Expressions menggunakan blok kode untuk menyederhanakan kode lima elemen menjadi satu pernyataan. orang kelas publik {private string givenname; nama keluarga string pribadi; usia int pribadi; jenis kelamin jenis kelamin pribadi; email string pribadi; telepon string pribadi; alamat string pribadi;} orang kelas publik {private string givenname; nama keluarga string pribadi; usia int pribadi; jenis kelamin jenis kelamin pribadi; email string pribadi; telepon string pribadi; alamat string pribadi;} Berikut ini adalah cara mengimplementasikan antarmuka pembanding menggunakan kelas dalam anonim dan ekspresi lambda: Public Class ComparArtortest {public static void main (string [] args) {list <Fon> personList = person.createShortList (); // Gunakan kelas dalam untuk mengimplementasikan penyortiran collections.sort (personlist, pembanding baru <fone> () {public int perbandingan (orang p1, orang p2) {return p1.getSurname (). CompareTo (p2.getSurname ());}}); System.out.println ("=== SURSI UNCE NAME ==="); untuk (orang p: personlist) {p.printname (); } // Implementasi Menggunakan ekspresi lambda // ascending system.out.println ("=== SURI UNC NAME ==="); Collections.sort (personlist, (orang p1, orang p2) -> p1.getSurname (). CompareTo (p2.getSurname ())); untuk (orang p: personlist) {p.printname (); } // desc sesaat System.out.println ("=== diurutkan desc nama ==="); Collections.sort (personlist, (p1, p2) -> p2.getSurname (). CompareTo (p1.getSurname ()))); untuk (orang p: personlist) {p.printname (); }}} Public Class ComparArtortest {public static void main (string [] args) {list <Fon> personList = person.createShortList (); // Gunakan kelas dalam untuk mengimplementasikan penyortiran collections.sort (personlist, pembanding baru <fone> () {public int perbandingan (orang p1, orang p2) {return p1.getSurname (). CompareTo (p2.getSurname ());}}); System.out.println ("=== SURSI UNCE NAME ==="); untuk (orang p: personlist) {p.printname (); } // Implementasi Menggunakan ekspresi lambda // ascending system.out.println ("=== SURI UNC NAME ==="); Collections.sort (personlist, (orang p1, orang p2) -> p1.getSurname (). CompareTo (p2.getSurname ())); untuk (orang p: personlist) {p.printname (); } // desc sesaat System.out.println ("=== diurutkan desc nama ==="); Collections.sort (personlist, (p1, p2) -> p2.getSurname (). CompareTo (p1.getSurname ()))); untuk (orang p: personlist) {p.printname (); }}} Anda dapat melihat bahwa kelas batin anonim dapat diimplementasikan melalui ekspresi Lambda. Perhatikan bahwa ekspresi Lambda pertama mendefinisikan jenis parameter sebagai orang; Ekspresi Lambda kedua menghilangkan definisi tipe. Lambda Expressions mendukung tipe knockdown, dan jika jenis yang diperlukan dapat disimpulkan melalui konteks, definisi jenis dapat dihilangkan. Di sini, karena kami menggunakan ekspresi lambda dalam pembanding yang menggunakan definisi generik, kompiler dapat menyimpulkan dua parameter ini sebagai orang.