Ekspresi lambda di Jawa

Halo, Habr! Untuk perhatian Anda saya persembahkan terjemahan dari artikel "Ekspresi Lambda Jawa" oleh penulis www.programiz.com .



pengantar



Dalam artikel ini, menggunakan contoh, kita akan menjelajahi ekspresi lambda di Java, penggunaannya dengan antarmuka fungsional, antarmuka fungsional berparameter, dan API Stream.



Ekspresi Lambda ditambahkan di Java 8. Tujuan utamanya adalah untuk meningkatkan keterbacaan dan mengurangi jumlah kode.



Tetapi sebelum beralih ke lambda, kita perlu memahami antarmuka fungsional.



Apa itu antarmuka fungsional?



Jika sebuah antarmuka di Java berisi satu dan hanya satu metode abstrak, maka itu disebut fungsional. Metode tunggal ini menentukan tujuan antarmuka.



Misalnya, antarmuka Runnable dari paket java.lang berfungsi karena hanya berisi satu metode run ().



Contoh 1: mendeklarasikan antarmuka fungsional di java



import java.lang.FunctionalInterface;
@FunctionalInterface
public interface MyInterface{
    //   
    double getValue();
}


Dalam contoh di atas, antarmuka MyInterface hanya memiliki satu metode abstrak, getValue (). Jadi antarmuka ini berfungsi.



Di sini kami telah menggunakan anotasiFungsional Antarmukayang membantu kompilator memahami bahwa antarmuka berfungsi. Oleh karena itu tidak memungkinkan lebih dari satu metode abstrak. Namun, kami dapat mengabaikannya.



Di Java 7, antarmuka fungsional diperlakukan sebagai Metode Abstrak Tunggal (SAM). SAM biasanya diimplementasikan menggunakan kelas anonim.



Contoh 2: menerapkan SAM dengan kelas anonim di java



public class FunctionInterfaceTest {
    public static void main(String[] args) {

        //  
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("      Runnable.")
            }
        }).start();
    }
}


Hasil eksekusi:



      Runnable.


Dalam contoh ini, kami menerima kelas anonim untuk memanggil metode tersebut. Ini membantu menulis program dengan lebih sedikit baris kode di Java 7. Namun, sintaksnya tetap cukup rumit dan tidak praktis.



Java 8 telah memperluas kemampuan SAM, selangkah lebih maju. Seperti yang kita ketahui, antarmuka fungsional hanya berisi satu metode, oleh karena itu, kita tidak perlu menentukan nama metode saat meneruskannya sebagai argumen. Inilah tepatnya yang memungkinkan ekspresi lambda untuk kita lakukan.



Pengantar ekspresi lambda



Ekspresi lambda pada dasarnya adalah kelas atau metode anonim. Ekspresi lambda tidak dijalankan sendiri. Sebaliknya, ini digunakan untuk mengimplementasikan metode yang ditentukan dalam antarmuka fungsional.



Bagaimana cara menulis ekspresi lambda di Java?



Di Java, ekspresi lambda memiliki sintaks berikut:



(parameter list) -> lambda body


Di sini kita telah menggunakan operator baru (->) - operator lambda. Mungkin sintaksnya tampak sedikit rumit. Mari kita ambil beberapa contoh.



Katakanlah kita memiliki metode seperti ini:



double getPiValue() {
    return 3.1415;
}


Kita bisa menulisnya menggunakan lambda seperti:



() -> 3.1415


Metode ini tidak memiliki parameter. Oleh karena itu, sisi kiri ekspresi berisi tanda kurung kosong. Sisi kanan adalah badan ekspresi lambda, yang mendefinisikan aksinya. Dalam kasus kami, nilai kembaliannya adalah 3,1415.



Jenis ekspresi lambda



Di Jawa, tubuh lambda bisa terdiri dari dua jenis.



1. Satu baris



() -> System.out.println("Lambdas are great");


2. Blok (multi-garis)



() -> {
    double pi = 3.1415;
    return pi;
};


Jenis ini memungkinkan ekspresi lambda memiliki beberapa operasi secara internal. Operasi ini harus diapit tanda kurung kurawal, diikuti dengan titik koma.



Catatan: Ekspresi lambda beberapa baris harus selalu memiliki pernyataan return, tidak seperti yang satu baris.



Contoh 3: Ekspresi Lambda



Mari kita tulis program Java yang mengembalikan Pi menggunakan ekspresi lambda.



Seperti yang dinyatakan sebelumnya, ekspresi lambda tidak dijalankan secara otomatis. Sebaliknya, ini merupakan implementasi dari metode abstrak yang dideklarasikan dalam antarmuka fungsional.



Jadi, pertama, kita perlu menjelaskan antarmuka fungsional.



import java.lang.FunctionalInterface;

//  
@FunctionalInterface
interface MyInterface{

    //  
    double getPiValue();
}
public class Main {

    public static void main( String[] args ) {

    //    MyInterface
    MyInterface ref;
    
    // -
    ref = () -> 3.1415;
    
    System.out.println("Value of Pi = " + ref.getPiValue());
    } 
}


Hasil eksekusi:



Value of Pi = 3.1415


Pada contoh di atas:



  • Kami telah membuat antarmuka fungsional, MyInterface, yang berisi satu metode abstrak, getPiValue ().
  • Di dalam kelas Utama, kami telah menyatakan referensi ke MyInterface. Perhatikan bahwa kita dapat mendeklarasikan referensi antarmuka, tetapi kita tidak dapat membuat objek darinya.



    //   
    	MyInterface ref = new myInterface();
    	//  
    	MyInterface ref;
    


  • Kemudian kami menetapkan ekspresi lambda ke tautan



    ref = () -> 3.1415;
  • Terakhir, kami memanggil metode getPiValue () menggunakan referensi antarmuka.



    System.out.println("Value of Pi = " + ref.getPiValue());


Ekspresi lambda dengan parameter



Sampai saat ini, kami telah membuat ekspresi lambda tanpa parameter apa pun. Namun, seperti halnya metode, lambda dapat memiliki parameter.



(n) -> (n % 2) == 0


Dalam contoh ini, variabel n di dalam tanda kurung adalah parameter yang diteruskan ke ekspresi lambda. Badan lambda mengambil parameter dan memeriksanya untuk paritas.



Contoh 4: menggunakan ekspresi lambda dengan parameter



@FunctionalInterface
interface MyInterface {

    //  
    String reverse(String n);
}

public class Main {

    public static void main( String[] args ) {

        //    MyInterface
        //  - 
        MyInterface ref = (str) -> {

            String result = "";
            for (int i = str.length()-1; i >= 0 ; i--)
            result += str.charAt(i);
            return result;
        };
        //    
        System.out.println("Lambda reversed = " + ref.reverse("Lambda"));
    }

}


Hasil eksekusi:



Lambda reversed = adbmaL


Antarmuka fungsional parameterized



Sampai saat ini, kami telah menggunakan antarmuka fungsional yang hanya menerima satu tipe nilai. Misalnya:



@FunctionalInterface
interface MyInterface {
    String reverseString(String n);
}


Antarmuka fungsional di atas hanya menerima String dan mengembalikan String. Namun, kami dapat membuat antarmuka kami umum untuk digunakan dengan tipe data apa pun.



Contoh 5: Antarmuka Parameter dan Ekspresi Lambda



//  
@FunctionalInterface
interface GenericInterface<T> {

    //  
    T func(T t);
}

public class Main {

    public static void main( String[] args ) {

        //     
        //   String
        //    
        GenericInterface<String> reverse = (str) -> {

            String result = "";
            for (int i = str.length()-1; i >= 0 ; i--)
            result += str.charAt(i);
            return result;
        };

        System.out.println("Lambda reversed = " + reverse.func("Lambda"));

        //     
        //   Integer
        //    
        GenericInterface<Integer> factorial = (n) -> {

            int result = 1;
            for (int i = 1; i <= n; i++)
            result = i * result;
            return result;
        };

        System.out.println("factorial of 5 = " + factorial.func(5));
    }
}


Hasil eksekusi:



Lambda reversed = adbmaL
factorial of 5 = 120


Dalam contoh ini, kami telah membuat antarmuka fungsional berparameter GenericInterface yang berisi metode func () berparameter.



Kemudian, di dalam kelas Utama:



  • GenericInterface <String> reverse - membuat tautan ke antarmuka yang bekerja dengan String.
  • GenericInterface <Integer> factorial - Membuat tautan ke antarmuka yang bekerja dengan Integer.


Lambda Expressions dan Stream API



JDK8 menambahkan paket baru, java.util.stream, yang memungkinkan pengembang java melakukan operasi seperti mencari, memfilter, mencocokkan, menggabungkan, atau memanipulasi koleksi seperti Daftar.



Misalnya, kami memiliki aliran data (dalam kasus kami, daftar string), di mana setiap string berisi nama negara dan kotanya. Sekarang kami dapat memproses aliran data ini dan hanya memilih kota-kota di Nepal.



Untuk melakukan ini, kita dapat menggunakan kombinasi Stream API dan ekspresi lambda.



Contoh 6: Menggunakan Lambdas di Stream API



import java.util.ArrayList;
import java.util.List;

public class StreamMain {

    //  
    static List<String> places = new ArrayList<>();

    //  
    public static List getPlaces(){

        //    
        places.add("Nepal, Kathmandu");
        places.add("Nepal, Pokhara");
        places.add("India, Delhi");
        places.add("USA, New York");
        places.add("Africa, Nigeria");

        return places;
    }

    public static void main( String[] args ) {

        List<String> myPlaces = getPlaces();
        System.out.println("Places from Nepal:");
        
        //  
        myPlaces.stream()
                .filter((p) -> p.startsWith("Nepal"))
                .map((p) -> p.toUpperCase())
                .sorted()
                .forEach((p) -> System.out.println(p));
    }

}


Hasil eksekusi:



Places from Nepal:
NEPAL, KATHMANDU
NEPAL, POKHARA


Dalam contoh di atas, perhatikan ekspresi ini:



myPlaces.stream()
        .filter((p) -> p.startsWith("Nepal"))
        .map((p) -> p.toUpperCase())
        .sorted()
        .forEach((p) -> System.out.println(p));


Di sini kami menggunakan metode seperti filter (), map (), forEach () dari Stream API, yang dapat menggunakan lambda sebagai parameter.



Juga, kita dapat mendeskripsikan ekspresi kita sendiri berdasarkan sintaks yang dijelaskan di atas. Ini akan memungkinkan kita untuk mengurangi jumlah baris kode.



All Articles