JavaRush /Java Blog /Random-ID /Ekspresi Lambda dengan contoh

Ekspresi Lambda dengan contoh

Dipublikasikan di grup Random-ID
Java pada awalnya adalah bahasa yang sepenuhnya berorientasi objek. Dengan pengecualian tipe primitif, semua yang ada di Java adalah sebuah objek. Bahkan array adalah objek. Contoh dari setiap kelas adalah objek. Tidak ada satu kemungkinan pun untuk mendefinisikan suatu fungsi secara terpisah (di luar kelas - kira-kira terjemahan ). Dan tidak ada cara untuk meneruskan suatu metode sebagai argumen atau mengembalikan isi metode sebagai hasil dari metode lain. Seperti itu. Tapi ini terjadi sebelum Java 8. Ekspresi Lambda dengan contoh - 1Sejak masa Swing yang lama, kelas anonim perlu ditulis ketika diperlukan untuk meneruskan beberapa fungsi ke metode tertentu. Misalnya, seperti inilah tampilan penambahan event handler:
someObject.addMouseListener(new MouseAdapter() {
            public void mouseClicked(MouseEvent e) {

                //Event listener implementation goes here...

            }
        });
Di sini kami ingin menambahkan beberapa kode ke pendengar acara mouse. Kami mendefinisikan kelas anonim MouseAdapterdan segera membuat objek darinya. Dengan cara ini, kami telah memasukkan fungsionalitas tambahan ke dalam addMouseListener. Singkatnya, tidak mudah untuk meneruskan metode (fungsi) sederhana di Java melalui argumen. Keterbatasan ini memaksa pengembang Java 8 untuk menambahkan fitur seperti ekspresi Lambda ke spesifikasi bahasa.

Mengapa Java memerlukan ekspresi Lambda?

Sejak awal, bahasa Java belum banyak berkembang, kecuali hal-hal seperti Anotasi, Generik, dll. Pertama-tama, Java selalu berorientasi objek. Setelah bekerja dengan bahasa fungsional seperti JavaScript, kita dapat memahami bagaimana Java benar-benar berorientasi objek dan diketik dengan kuat. Fungsi tidak diperlukan di Java. Dengan sendirinya, mereka tidak dapat ditemukan di dunia Jawa. Dalam bahasa pemrograman fungsional, fungsi diutamakan. Mereka ada dengan sendirinya. Anda dapat menetapkannya ke variabel dan meneruskannya melalui argumen ke fungsi lain. JavaScript adalah salah satu contoh terbaik dari bahasa pemrograman fungsional. Anda dapat menemukan artikel bagus di Internet yang merinci manfaat JavaScript sebagai bahasa fungsional. Bahasa fungsional memiliki alat canggih seperti Penutupan, yang memberikan sejumlah keunggulan dibandingkan metode penulisan aplikasi tradisional. Penutupan adalah fungsi dengan lingkungan yang melekat padanya - tabel yang menyimpan referensi ke semua variabel non-lokal dari fungsi tersebut. Di Java, penutupan dapat disimulasikan melalui ekspresi Lambda. Tentu saja, ada perbedaan antara penutupan dan ekspresi Lambda, dan bukan perbedaan kecil, namun ekspresi lambda adalah alternatif yang baik untuk penutupan. Dalam blognya yang sarkastik dan lucu, Steve Yegge menjelaskan bagaimana dunia Java sangat terikat dengan kata benda (entitas, objek - kira-kira terjemahan ). Jika Anda belum membaca blognya, saya merekomendasikannya. Dia menjelaskan dengan cara yang lucu dan menarik alasan sebenarnya mengapa ekspresi Lambda ditambahkan ke Java. Ekspresi Lambda menghadirkan fungsionalitas ke Java yang telah lama hilang. Ekspresi Lambda menghadirkan fungsionalitas ke bahasa seperti halnya objek. Meskipun hal ini tidak 100% benar, Anda dapat melihat bahwa ekspresi Lambda, meskipun bukan merupakan penutupan, memberikan kemampuan serupa. Dalam bahasa fungsional, ekspresi lambda adalah fungsi; namun di Java, ekspresi lambda diwakili oleh objek, dan harus dikaitkan dengan tipe objek tertentu yang disebut antarmuka fungsional. Selanjutnya kita akan melihat apa itu. Artikel Mario Fusco “Mengapa kita membutuhkan Ekspresi Lambda di Java” menjelaskan secara rinci mengapa semua bahasa modern memerlukan kemampuan penutupan.

Pengantar Ekspresi Lambda

Ekspresi Lambda adalah fungsi anonim (mungkin bukan definisi yang 100% benar untuk Java, tetapi ini memberikan kejelasan). Sederhananya, ini adalah metode tanpa deklarasi, mis. tanpa pengubah akses, mengembalikan nilai dan nama. Singkatnya, mereka memungkinkan Anda menulis suatu metode dan segera menggunakannya. Hal ini sangat berguna dalam kasus pemanggilan metode satu kali, karena mengurangi waktu yang diperlukan untuk mendeklarasikan dan menulis suatu metode tanpa harus membuat kelas. Ekspresi Lambda di Java biasanya memiliki sintaks berikut (аргументы) -> (тело). Misalnya:
(арг1, арг2...) -> { тело }

(тип1 арг1, тип2 арг2...) -> { тело }
Berikut adalah beberapa contoh ekspresi Lambda yang sebenarnya:
(int a, int b) -> {  return a + b; }

() -> System.out.println("Hello World");

(String s) -> { System.out.println(s); }

() -> 42

() -> { return 3.1415 };

Struktur Ekspresi Lambda

Mari pelajari struktur ekspresi lambda:
  • Ekspresi Lambda dapat memiliki 0 atau lebih parameter masukan.
  • Jenis parameter dapat ditentukan secara eksplisit atau dapat diperoleh dari konteksnya. Misalnya ( int a) dapat ditulis seperti ini ( a)
  • Parameter diapit tanda kurung dan dipisahkan dengan koma. Misalnya ( a, b) atau ( int a, int b) atau ( String a, int b, float c)
  • Jika tidak ada parameter, maka Anda perlu menggunakan tanda kurung kosong. Misalnya() -> 42
  • Jika hanya ada satu parameter, jika tipenya tidak ditentukan secara eksplisit, tanda kurung dapat dihilangkan. Contoh:a -> return a*a
  • Isi ekspresi Lambda dapat berisi 0 ekspresi atau lebih.
  • Jika isi pernyataan terdiri dari satu pernyataan, pernyataan tersebut tidak boleh diapit kurung kurawal, dan nilai yang dikembalikan dapat ditentukan tanpa kata kunci return.
  • Jika tidak, kurung kurawal diperlukan (blok kode) dan nilai yang dikembalikan harus ditentukan di akhir menggunakan kata kunci return(jika tidak, jenis pengembaliannya adalah void).

Apa itu antarmuka fungsional

Di Java, antarmuka Marker adalah antarmuka tanpa mendeklarasikan metode atau bidang. Dengan kata lain, antarmuka token adalah antarmuka kosong. Demikian pula, Antarmuka Fungsional adalah antarmuka dengan hanya satu metode abstrak yang dideklarasikan di dalamnya. java.lang.Runnableadalah contoh antarmuka fungsional. Itu hanya mendeklarasikan satu metode void run(). Ada juga antarmuka ActionListener- juga fungsional. Sebelumnya, kita harus menggunakan kelas anonim untuk membuat objek yang mengimplementasikan antarmuka fungsional. Dengan ekspresi Lambda, segalanya menjadi lebih sederhana. Setiap ekspresi lambda dapat terikat secara implisit ke beberapa antarmuka fungsional. Misalnya, Anda bisa membuat referensi ke Runnableantarmuka, seperti yang ditunjukkan dalam contoh berikut:
Runnable r = () -> System.out.println("hello world");
Konversi semacam ini selalu dilakukan secara implisit ketika kita tidak menentukan antarmuka fungsional:
new Thread(
    () -> System.out.println("hello world")
).start();
Pada contoh di atas, compiler secara otomatis membuat ekspresi lambda sebagai implementasi Runnableantarmuka dari konstruktor kelas Thread: public Thread(Runnable r) { }. Berikut beberapa contoh ekspresi lambda dan antarmuka fungsional terkait:
Consumer<Integer> c = (int x) -> { System.out.println(x) };

BiConsumer<Integer, String> b = (Integer x, String y) -> System.out.println(x + " : " + y);

Predicate<String> p = (String s) -> { s == null };
Anotasi @FunctionalInterfaceyang ditambahkan di Java 8 sesuai dengan Spesifikasi Bahasa Java memeriksa apakah antarmuka yang dideklarasikan berfungsi. Selain itu, Java 8 menyertakan sejumlah antarmuka fungsional siap pakai untuk digunakan dengan ekspresi Lambda. @FunctionalInterfaceakan menimbulkan kesalahan kompilasi jika antarmuka yang dinyatakan tidak berfungsi. Berikut ini adalah contoh pendefinisian antarmuka fungsional:
@FunctionalInterface
public interface WorkerInterface {

    public void doSomeWork();

}
Seperti definisinya, antarmuka fungsional hanya dapat memiliki satu metode abstrak. Jika Anda mencoba menambahkan metode abstrak lain, Anda akan mendapatkan kesalahan kompilasi. Contoh:
@FunctionalInterface
public interface WorkerInterface {

    public void doSomeWork();

    public void doSomeMoreWork();

}
Kesalahan
Unexpected @FunctionalInterface annotation
    @FunctionalInterface ^ WorkerInterface is not a functional interface multiple
    non-overriding abstract methods found in interface WorkerInterface 1 error
После определения функционального интерфейса, мы можем его использовать и получать все преимущества Lambda-выражений. Пример:// defining a functional interface
@FunctionalInterface
public interface WorkerInterface {

    public void doSomeWork();

}
public class WorkerInterfaceTest {

    public static void execute(WorkerInterface worker) {
        worker.doSomeWork();
    }

    public static void main(String [] args) {

      // calling the doSomeWork method via an anonymous class
      // (classic)
      execute(new WorkerInterface() {
            @Override
            public void doSomeWork() {
               System.out.println("Worker called via an anonymous class");
            }
        });

      // calling the doSomeWork method via Lambda expressions
      // (Java 8 new)
      execute( () -> System.out.println("Worker called via Lambda") );
    }

}
Kesimpulan:
Worker вызван через анонимный класс
Worker вызван через Lambda
Di sini kita telah mendefinisikan antarmuka fungsional kita sendiri dan menggunakan ekspresi lambda. Metode ini execute()mampu menerima ekspresi lambda sebagai argumen.

Contoh ekspresi Lambda

Cara terbaik untuk memahami ekspresi Lambda adalah dengan melihat beberapa contoh: Aliran Threaddapat diinisialisasi dengan dua cara:
// Old way:
new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("Hello from thread");
    }
}).start();
// New way:
new Thread(
    () -> System.out.println("Hello from thread")
).start();
Manajemen acara di Java 8 juga dapat dilakukan melalui ekspresi Lambda. Berikut adalah dua cara untuk menambahkan event handler ActionListenerke komponen UI:
// Old way:
button.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        System.out.println("Button pressed. Old way!");
    }
});
// New way:
button.addActionListener( (e) -> {
        System.out.println("Button pressed. Lambda!");
});
Contoh sederhana menampilkan semua elemen array tertentu. Perhatikan bahwa ada lebih dari satu cara untuk menggunakan ekspresi lambda. Di bawah ini kami membuat ekspresi lambda dengan cara biasa menggunakan sintaks panah, dan kami juga menggunakan operator titik dua (::), yang di Java 8 mengubah metode biasa menjadi ekspresi lambda:
// Old way:
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
for(Integer n: list) {
    System.out.println(n);
}
// New way:
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
list.forEach(n -> System.out.println(n));
// New way using double colon operator ::
list.forEach(System.out::println);
Dalam contoh berikut, kami menggunakan antarmuka fungsional Predicateuntuk membuat pengujian dan mencetak item yang lulus pengujian tersebut. Dengan cara ini Anda dapat memasukkan logika ke dalam ekspresi lambda dan melakukan berbagai hal berdasarkan logika tersebut.
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;

public class Main {

    public static void main(String [] a)  {

        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);

        System.out.print("Outputs all numbers: ");
        evaluate(list, (n)->true);

        System.out.print("Does not output any number: ");
        evaluate(list, (n)->false);

        System.out.print("Output even numbers: ");
        evaluate(list, (n)-> n%2 == 0 );

        System.out.print("Output odd numbers: ");
        evaluate(list, (n)-> n%2 == 1 );

        System.out.print("Output numbers greater than 5: ");
        evaluate(list, (n)-> n > 5 );

    }

    public static void evaluate(List<Integer> list, Predicate<Integer> predicate) {
        for(Integer n: list)  {
            if(predicate.test(n)) {
                System.out.print(n + " ");
            }
        }
        System.out.println();
    }

}
Kesimpulan:
Выводит все числа: 1 2 3 4 5 6 7
Не выводит ни одного числа:
Вывод четных чисел: 2 4 6
Вывод нечетных чисел: 1 3 5 7
Вывод чисел больше 5: 6 7
Dengan mengutak-atik ekspresi Lambda, Anda dapat menampilkan kuadrat setiap elemen daftar. Perhatikan bahwa kami menggunakan metode ini stream()untuk mengubah daftar biasa menjadi aliran. Java 8 menyediakan kelas yang luar biasa Stream( java.util.stream.Stream). Ini berisi banyak metode berguna yang dapat Anda gunakan dengan ekspresi lambda. Kami meneruskan ekspresi lambda x -> x*xke metode map(), yang menerapkannya ke semua elemen dalam aliran. Setelah itu kita gunakan forEachuntuk mencetak semua elemen daftar.
// Old way:
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7);
for(Integer n : list) {
    int x = n * n;
    System.out.println(x);
}
// New way:
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7);
list.stream().map((x) -> x*x).forEach(System.out::println);
Dengan adanya daftar, Anda perlu mencetak jumlah kuadrat semua elemen daftar. Ekspresi Lambda memungkinkan Anda mencapai hal ini hanya dengan menulis satu baris kode. Contoh ini menggunakan metode konvolusi (reduksi) reduce(). Kita menggunakan metode map()untuk mengkuadratkan setiap elemen, lalu menggunakan metode reduce()untuk menciutkan semua elemen menjadi satu angka.
// Old way:
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7);
int sum = 0;
for(Integer n : list) {
    int x = n * n;
    sum = sum + x;
}
System.out.println(sum);
// New way:
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7);
int sum = list.stream().map(x -> x*x).reduce((x,y) -> x + y).get();
System.out.println(sum);

Perbedaan antara ekspresi Lambda dan kelas anonim

Perbedaan utamanya adalah penggunaan kata kunci this. Untuk kelas anonim, kata kunci '' thismenunjukkan objek kelas anonim, sedangkan dalam ekspresi lambda, ' this' menunjukkan objek kelas yang menggunakan ekspresi lambda. Perbedaan lainnya adalah cara penyusunannya. Java mengkompilasi ekspresi lambda dan mengubahnya menjadi privatemetode kelas. Ini menggunakan instruksi invokedynamic , yang diperkenalkan di Java 7 untuk pengikatan metode dinamis. Tal Weiss menjelaskan di blognya bagaimana Java mengkompilasi ekspresi lambda menjadi bytecode

Kesimpulan

Mark Reinhold (Kepala Arsitek Oracle) menyebut ekspresi Lambda sebagai perubahan paling signifikan dalam model pemrograman yang pernah terjadi - bahkan lebih signifikan daripada generik. Dia pasti benar, karena... mereka memberi pemrogram Java kemampuan bahasa pemrograman fungsional yang telah ditunggu-tunggu semua orang. Seiring dengan inovasi seperti metode ekstensi Virtual, ekspresi Lambda memungkinkan Anda menulis kode berkualitas sangat tinggi. Saya harap artikel ini memberi Anda gambaran tentang Java 8. Semoga berhasil :)
Komentar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION