Lambda dan Function Anonim Kotlin

Muhammad Fajri
Tuesday, 18 May 2021

Bismillahirrahmanirrahim.

Lambda dan function anonim disebut sebagai function literal. Function ini tidak dideklarasikan tetapi diterusakan secara langsung sebagai sebuah ekspresi. Karena itu ia tidak memerlukan sebuah nama. Berikut sebuah contoh function tanpa nama.

val doThis = {
    println("action")
}

Untuk memanggil function tersebut, dapat dilakukan seperti berikut, dengan anggapan pemanggilan pada function main atau pun function tingkat teratas yang lainnya.

doThis()

// or
doThis.invoke()

Cara pemanggilan yang pertama terlihat lebih baik, dengan demikian kita sebaiknya menggunakan yang itu saja. Namun, dengan cara ini terlihat istimewa ketika digunakan dalam konteks higher order function.

Berikutnya, kita menulis ulang function doThis() ini, di samping kita mendeklarasikan dan mendefinisikan lambda ternama, kita akan meneruskannya sebagai sebuah argumen pada sebuah higher order function dengan nama executor.

fun main() {
    executor(
        { println("do this") }
    )
}

fun executor(action:() -> Unit) {
    action()
}

Pada { println("do this") } merupakan function literal kita meneruskan doThis yang merupakan properti yang nilainya merupakan ekspresi lambda. Pada contoh ini, kita meneruskan ekspresi lambda secara langsung pada higher order function. Ekspresi lambda dimuat dalam sebuah pasangan kurung kurawal.

Ekspresi Lambda dengan Parameter

Contoh lain untuk menuliskan sebuah function yang memiliki parameter yang bukan lambda, dan ingin menuliskannya menjadi ekspresi lambda.

fun display(msg:String) {
    println("Hello $msg")
}

Jika dituliskan sebagai lambda:

{ msg:String -> println("Hello $msg") }

Dapat dilihat perbedaan pada kedua kode di atas. Jika ditulis dalam ekspresi lambda header, kata kunci fun dan nama function dihilangkan, dan daftar parameter ditulis ulang di dalam ekspresi lambda.

Pada ekspresi lambda, daftar parameter dituliskan di sebelah kiri operator arrow dan body function ditemukan di bagian kanan. Dapat dilihat pula bahwa parameter dari lambda tidak perlu dituliskan dalam tanda kurung karena operator arrow telah memisahkan daftar parameter dari body lambda.

Pada kasus tertentu, kita dapat menuliskan ekspresi lambda tanpa disertai tipe data pada parameternya bahkan tanpa operator arrow jika hanya memiliki satu buah parameter.

// tanpa tipe data pada parameternya
{ msg -> println("Hello $msg") }

// tanpa operator arrow
{ println("Hello $msg") }

// atau
{ println("Hello $it") }

Kita dapat memilih menggunakan penulisan lambda seperti di atas selama hanya memiliki satu parameter. Untuk penulisan pada bagian ketiga mengganti nama parameter $msg dengan $it dapat dipilih karena secara konteks, function dapat mengacu satu parameter tersebut. Dengan demikian, penulisan lengkap kode program ekspresi lambda yang memiliki satu parameter yaitu sebagai berikut.

fun main() {
    executor({ println("Hello $it") })
}

fun executor(display:(msg:String) -> Unit) {
    display("World")
}

Lambda dengan lebih dari satu parameter.

fun main() {
    doer({ x,y -> println(x + y) })
}

fun doer(sum:(x:Int,y:Int) -> Unit) {
    sum(1,2)
}

Contoh lain di mana higher order function memiliki beberapa parameter bersamaan dengan tipe data function.

fun executor(arg: String = "Mondo", display: (msg: String) -> Unit) {
    display(arg)
}

// untuk pemanggilannya
executor("Earth", { println("Hola $it") })

// pemanggilan nilai default
executor({ println("Hola $it") })

// jika lambda sebagai parameter terakhir
executor() { println("Hola $it") }

// atau jika hanya ada lambda sebagai parameter
executor { println("Hola $it") }

Pada pemanggilan yang kedua, dengan cara itu nilai default yang akan ditampilkan. Jika parameter higher order function memiliki ekspresi lambda sebagai parameter terakhir, dapat menggunakan pemanggilan pada cara ketiga. Atau jika pada higher order function hanya memiliki lambda sebagai parameter maka dapat menggunakan cara yang keempat di mana tanda kurung pada nama function dihilangkan.

Closure pada Lambda

Saat menggunakan lambda dalam sebuah function, lambda dapat mengakses closure dari function tersebut. Closure terdiri dari variabel lokal pada cakupan luar dan semua parameter yang berada di dalam function. Contoh:

fun main() {
    executor(listOf(1..1000).flatten())
}

fun executor(numbers: List<Int>) {
    var sum = 0
    numbers.forEach {
        if(it % 2 == 0) {
            sum += it
        }
    }
    println("Sum of all even numbers = $sum")
}

Output:

Sum of all even numbers = 250500

Pada bagian executor(listOf(1..1000).flatten()) kita meneruskan list of Int pada function executor(). Menggunakan function rangeTo dalam bentuk operator (..) merupakan cara yang mudah untuk menghasilkan list of integer dari 1 sampai 1000, kita harus menggunakan function flatten() untuk menjadikkan deretan angka menjadi list of Int.

forEach merupakan higher order function, berada dalam lambda. Ini memungkinkan kita untuk menelusuri tiap item dalam list. forEach hanya memiliki satu buah parameter dan kita dapat mengakses parameter tersebut menggunakan nama implisit it.

Variabel sum merupakan bagian dari closure, berada di dalam body dari function dimana lambda didefinisikan. Lambda memiliki akses pada closure tersebut.

Di dalam lambda pada Java, kita hanya dapat mengakses sebuah variabel dalam closure-nya jika variabel tersebut bersifat final. Di Kotlin, tidak ada batasan seperti itu.

Referensi

  1. Hagos, Ted. 2018. Learn Android Studio 3 with Kotlin: Efficient Android App Development. Apress: Manila.