1. Antarmuka

Untuk memahami apa itu fungsi lambda, Anda harus terlebih dahulu memahami apa itu antarmuka. Jadi, mari kita mengingat kembali poin-poin utamanya.

Antarmuka adalah variasi dari konsep kelas. Kelas yang sangat terpotong, katakanlah. Tidak seperti kelas, antarmuka tidak dapat memiliki variabelnya sendiri (kecuali yang statis). Anda juga tidak dapat membuat objek yang jenisnya adalah antarmuka:

  • Anda tidak dapat mendeklarasikan variabel kelas
  • Anda tidak dapat membuat objek

Contoh:

interface Runnable
{
   void run();
}
Contoh antarmuka standar

Menggunakan antarmuka

Jadi mengapa diperlukan antarmuka? Antarmuka hanya digunakan bersama dengan pewarisan. Antarmuka yang sama dapat diwarisi oleh kelas yang berbeda, atau seperti yang juga dikatakan — kelas mengimplementasikan antarmuka .

Jika sebuah kelas mengimplementasikan antarmuka, ia harus mengimplementasikan metode yang dideklarasikan oleh tetapi tidak diimplementasikan oleh antarmuka. Contoh:

interface Runnable
{
   void run();
}

class Timer implements Runnable
{
   void run()
   {
      System.out.println(LocalTime.now());
   }
}

class Calendar implements Runnable
{
   void run()
   {
      var date = LocalDate.now();
      System.out.println("Today: " + date.getDayOfWeek());
   }
}

Kelas Timermengimplementasikan Runnableantarmuka, sehingga harus mendeklarasikan di dalam dirinya sendiri semua metode yang ada di antarmuka Runnabledan mengimplementasikannya, yaitu menulis kode di badan metode. Hal yang sama berlaku untuk Calendarkelas.

Tapi sekarang Runnablevariabel dapat menyimpan referensi ke objek yang mengimplementasikan Runnableantarmuka.

Contoh:

Kode Catatan
Timer timer = new Timer();
timer.run();

Runnable r1 = new Timer();
r1.run();

Runnable r2 = new Calendar();
r2.run();

Metode run()di Timerkelas akan dipanggil


Metode run()di Timerkelas akan dipanggil


Metode run()di Calendarkelas akan dipanggil

Anda selalu dapat menetapkan referensi objek ke variabel tipe apa pun, selama tipe tersebut adalah salah satu kelas leluhur objek. Untuk kelas Timerand Calendar, ada dua jenis: Objectdan Runnable.

Jika Anda menetapkan referensi objek ke variabel Object, Anda hanya dapat memanggil metode yang dideklarasikan di Objectkelas. Dan jika Anda menetapkan referensi objek ke variabel Runnable, Anda dapat memanggil metode tipe tersebut Runnable.

Contoh 2:

ArrayList<Runnable> list = new ArrayList<Runnable>();
list.add (new Timer());
list.add (new Calendar());

for (Runnable element: list)
    element.run();

Kode ini akan berfungsi, karena objek Timerdan Calendartelah menjalankan metode yang berfungsi dengan baik. Jadi, memanggil mereka bukanlah masalah. Jika kita baru saja menambahkan metode run() ke kedua kelas, maka kita tidak akan bisa memanggilnya dengan cara yang begitu sederhana.

Pada dasarnya Runnableinterface hanya digunakan sebagai tempat meletakkan method run.



2. Menyortir

Mari beralih ke sesuatu yang lebih praktis. Sebagai contoh, mari kita lihat pengurutan string.

Untuk mengurutkan kumpulan string berdasarkan abjad, Java memiliki metode hebat bernamaCollections.sort(collection);

Metode statis ini mengurutkan koleksi yang diteruskan. Dan dalam proses penyortiran, ia melakukan perbandingan berpasangan dari elemen-elemennya untuk memahami apakah elemen harus ditukar.

Selama penyortiran, perbandingan ini dilakukan dengan menggunakan compareTometode () , yang dimiliki semua kelas standar: Integer, String, ...

Metode compareTo() dari kelas Integer membandingkan nilai dari dua angka, sedangkan metode compareTo() dari kelas String melihat urutan abjad dari string.

Jadi kumpulan angka akan diurutkan secara menaik, sedangkan kumpulan string akan diurutkan berdasarkan abjad.

Algoritma pengurutan alternatif

Tetapi bagaimana jika kita ingin mengurutkan string bukan berdasarkan abjad, tetapi menurut panjangnya? Dan bagaimana jika kita ingin mengurutkan angka dalam urutan menurun? Apa yang Anda lakukan dalam kasus ini?

Untuk menangani situasi seperti itu, Collectionskelas memiliki sort()metode lain yang memiliki dua parameter:

Collections.sort(collection, comparator);

Di mana komparator adalah objek khusus yang mengetahui cara membandingkan objek dalam koleksi selama operasi pengurutan . Istilah pembanding berasal dari bahasa Inggris pembanding kata , yang pada gilirannya berasal dari membandingkan , yang berarti "membandingkan".

Jadi apa objek khusus ini?

Comparatorantarmuka

Yah, itu semua sangat sederhana. Jenis sort()parameter kedua metode ini adalahComparator<T>

Di mana T adalah parameter tipe yang menunjukkan tipe elemen dalam koleksi , dan Comparatormerupakan antarmuka yang memiliki metode tunggalint compare(T obj1, T obj2);

Dengan kata lain, objek pembanding adalah objek apa pun dari kelas apa pun yang mengimplementasikan antarmuka Pembanding. Antarmuka Comparator terlihat sangat sederhana:

public interface Comparator<Type>
{
   public int compare(Type obj1, Type obj2);
}
Kode untuk antarmuka Komparator

Metode compare()membandingkan dua argumen yang diteruskan ke sana.

Jika metode mengembalikan angka negatif, artinya obj1 < obj2. Jika metode mengembalikan angka positif, artinya obj1 > obj2. Jika metode mengembalikan 0, itu berarti obj1 == obj2.

Berikut adalah contoh objek pembanding yang membandingkan string berdasarkan panjangnya:

public class StringLengthComparator implements Comparator<String>
{
   public int compare (String obj1, String obj2)
   {
      return obj1.length() – obj2.length();
   }
}
Kode StringLengthComparatorkelas

Untuk membandingkan panjang string, kurangi satu panjang dari yang lain.

Kode lengkap untuk program yang mengurutkan string berdasarkan panjangnya akan terlihat seperti ini:

public class Solution
{
   public static void main(String[] args)
   {
      ArrayList<String> list = new ArrayList<String>();
      Collections.addAll(list, "Hello", "how's", "life?");
      Collections.sort(list, new StringLengthComparator());
   }
}

class StringLengthComparator implements Comparator<String>
{
   public int compare (String obj1, String obj2)
   {
      return obj1.length() – obj2.length();
   }
}
Menyortir string berdasarkan panjangnya


3. Gula sintaksis

Bagaimana menurut Anda, bisakah kode ini ditulis lebih kompak? Pada dasarnya, hanya ada satu baris yang berisi informasi berguna — obj1.length() - obj2.length();.

Tapi kode tidak bisa ada di luar metode, jadi kami harus menambahkan compare()metode, dan untuk menyimpan metode kami harus menambahkan kelas baru — StringLengthComparator. Dan kita juga perlu menentukan jenis variabelnya... Semuanya tampak benar.

Tapi ada cara untuk membuat kode ini lebih pendek. Kami punya beberapa gula sintaksis untuk Anda. Dua sendok!

Kelas batin anonim

Anda dapat menulis kode pembanding tepat di dalam main()metode, dan kompiler akan melakukan sisanya. Contoh:

public class Solution
{
    public static void main(String[] args)
    {
        ArrayList<String> list = new ArrayList<String>();
        Collections.addAll(list, "Hello", "how's", "life?");

        Comparator<String> comparator = new Comparator<String>()
        {
            public int compare (String obj1, String obj2)
            {
                return obj1.length() – obj2.length();
            }
        };

        Collections.sort(list, comparator);
    }
}
Sortir string berdasarkan panjangnya

Anda dapat membuat objek yang mengimplementasikan Comparatorantarmuka tanpa membuat kelas secara eksplisit! Kompiler akan membuatnya secara otomatis dan memberinya nama sementara. Mari kita bandingkan:

Comparator<String> comparator = new Comparator<String>()
{
    public int compare (String obj1, String obj2)
    {
        return obj1.length() – obj2.length();
    }
};
Kelas batin anonim
Comparator<String> comparator = new StringLengthComparator();

class StringLengthComparator implements Comparator<String>
{
    public int compare (String obj1, String obj2)
    {
        return obj1.length() – obj2.length();
    }
}
StringLengthComparatorkelas

Warna yang sama digunakan untuk menunjukkan blok kode yang identik dalam dua kasus yang berbeda. Perbedaannya cukup kecil dalam praktiknya.

Ketika kompiler menemukan blok kode pertama, itu hanya menghasilkan blok kode kedua yang sesuai dan memberi kelas beberapa nama acak.


4. Ekspresi Lambda di Jawa

Katakanlah Anda memutuskan untuk menggunakan kelas dalam anonim dalam kode Anda. Dalam hal ini, Anda akan memiliki blok kode seperti ini:

Comparator<String> comparator = new Comparator<String>()
{
    public int compare (String obj1, String obj2)
    {
        return obj1.length() – obj2.length();
    }
};
Kelas batin anonim

Di sini kami menggabungkan deklarasi variabel dengan pembuatan kelas anonim. Tapi ada cara untuk membuat kode ini lebih pendek. Misalnya, seperti ini:

Comparator<String> comparator = (String obj1, String obj2) ->
{
    return obj1.length() – obj2.length();
};

Titik koma diperlukan karena di sini kita tidak hanya memiliki deklarasi kelas implisit, tetapi juga pembuatan variabel.

Notasi seperti ini disebut ekspresi lambda.

Jika kompiler menemukan notasi seperti ini dalam kode Anda, itu hanya menghasilkan versi verbose dari kode (dengan kelas dalam anonim).

Perhatikan bahwa saat menulis ekspresi lambda, kami menghilangkan tidak hanya nama kelasnya , tetapi juga nama metodenya .Comparator<String>int compare()

Kompilasi tidak akan mengalami masalah dalam menentukan metode , karena ekspresi lambda hanya dapat ditulis untuk antarmuka yang memiliki metode tunggal . Ngomong-ngomong, ada cara untuk menyiasati aturan ini, tetapi Anda akan mempelajarinya saat mulai mempelajari OOP secara lebih mendalam (kita berbicara tentang metode default).

Mari kita lihat versi verbose kode lagi, tetapi kita akan menghilangkan bagian yang dapat dihilangkan saat menulis ekspresi lambda:

Comparator<String> comparator = new Comparator<String>()
{
    public int compare (String obj1, String obj2)
   {
      return obj1.length() – obj2.length();
   }
};
Kelas batin anonim

Sepertinya tidak ada hal penting yang terlewatkan. Memang, jika Comparatorantarmuka hanya memiliki satu compare()metode, kompiler dapat sepenuhnya memulihkan kode berwarna abu-abu dari kode yang tersisa.

Penyortiran

Omong-omong, sekarang kita bisa menulis kode pengurutan seperti ini:

Comparator<String> comparator = (String obj1, String obj2) ->
{
   return obj1.length() – obj2.length();
};
Collections.sort(list, comparator);

Atau bahkan seperti ini:

Collections.sort(list, (String obj1, String obj2) ->
   {
      return obj1.length() – obj2.length();
   }
);

Kami hanya segera mengganti comparatorvariabel dengan nilai yang diberikan ke variabel comparator.

Ketik inferensi

Tapi itu belum semuanya. Kode dalam contoh ini dapat ditulis dengan lebih ringkas. Pertama, compiler dapat menentukan sendiri bahwa variabel obj1and obj2adalah Strings. Dan kedua, kurung kurawal dan pernyataan pengembalian juga dapat dihilangkan jika Anda hanya memiliki satu perintah dalam kode metode.

Versi singkatnya akan seperti ini:

Comparator<String> comparator = (obj1, obj2) ->
   obj1.length() – obj2.length();

Collections.sort(list, comparator);

Dan jika alih-alih menggunakan comparatorvariabel, kami langsung menggunakan nilainya, maka kami mendapatkan versi berikut:

Collections.sort(list, (obj1, obj2) ->  obj1.length() — obj2.length() );

Nah, apa pendapat Anda tentang itu? Hanya satu baris kode tanpa informasi berlebihan — hanya variabel dan kode. Tidak ada cara untuk membuatnya lebih pendek! Atau ada?



5. Cara kerjanya

Bahkan, kodenya bisa ditulis lebih kompak. Tapi lebih dari itu nanti.

Anda dapat menulis ekspresi lambda di mana Anda akan menggunakan tipe antarmuka dengan satu metode.

Misalnya, dalam kode , Anda dapat menulis ekspresi lambda karena tanda tangan metodenya adalah seperti ini:Collections.sort(list, (obj1, obj2) -> obj1.length() - obj2.length());sort()

sort(Collection<T> colls, Comparator<T> comp)

Saat kami meneruskan ArrayList<String>koleksi sebagai argumen pertama ke metode pengurutan, kompiler dapat menentukan bahwa jenis argumen kedua adalah . Dan dari sini, disimpulkan bahwa antarmuka ini memiliki metode tunggal. Segala sesuatu yang lain adalah teknis.Comparator<String>int compare(String obj1, String obj2)