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();
}
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 Timer
mengimplementasikan Runnable
antarmuka, sehingga harus mendeklarasikan di dalam dirinya sendiri semua metode yang ada di antarmuka Runnable
dan mengimplementasikannya, yaitu menulis kode di badan metode. Hal yang sama berlaku untuk Calendar
kelas.
Tapi sekarang Runnable
variabel dapat menyimpan referensi ke objek yang mengimplementasikan Runnable
antarmuka.
Contoh:
Kode | Catatan |
---|---|
|
Metode run() di Timer kelas akan dipanggil Metode run() di Timer kelas akan dipanggil Metode run() di Calendar kelas akan dipanggil |
Anda selalu dapat menetapkan referensi objek ke variabel tipe apa pun, selama tipe tersebut adalah salah satu kelas leluhur objek. Untuk kelas Timer
and Calendar
, ada dua jenis: Object
dan Runnable
.
Jika Anda menetapkan referensi objek ke variabel Object
, Anda hanya dapat memanggil metode yang dideklarasikan di Object
kelas. 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 Timer
dan Calendar
telah 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 Runnable
interface 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 compareTo
metode () , 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, Collections
kelas 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?
Comparator
antarmuka
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 Comparator
merupakan 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);
}
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();
}
}
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();
}
}
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);
}
}
Anda dapat membuat objek yang mengimplementasikan Comparator
antarmuka 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();
}
};
Comparator<String> comparator = new StringLengthComparator();
class StringLengthComparator implements Comparator<String>
{
public int compare (String obj1, String obj2)
{
return obj1.length() – obj2.length();
}
}
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();
}
};
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();
}
};
Sepertinya tidak ada hal penting yang terlewatkan. Memang, jika Comparator
antarmuka 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 comparator
variabel 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 obj1
and obj2
adalah 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 comparator
variabel, 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)
GO TO FULL VERSION