Memahami Scope Functions pada Kotlin

Wuriyanto
TelkomDev
Published in
6 min readJan 17, 2021
scope function run

Pada standard library Kotlin sudah terdapat beberapa fungsi yang bisa kita manfaatkan untuk mengeksekusi sebuah scope code dalam konteks sebuah objek. Fungsi-fungsi ini disebut dengan scope functions. Dengan memanfaatkan scope functions, code yang kita buat akan lebih simpel(mengurangi boilerplate) dan readable. Tetapi sebelum kita bahas lebih lanjut topik scope function ini, saya asumsikan anda sudah paham beberapa topik seperti extensions function pada Kotlin, higher order function dan anonymous function/lambda.

Untuk sedikit lebih ada gambaran tentang extensions function, higher order function dan anonymous function/lambda pada Kotlin, kita akan bahas sedikit satu persatu topik tersebut.

First class citizen function dan higher order function

Function pada Kotlin by default adalah first-class-citizen seperti pada bahasa non fungsional murni seperti Go, Javascript, Rust. First-class-citizen yang berarti fungsi pada Kotlin bisa ditempatkan dimanapun, seperti didalam variabel, struktur data(List, Map, dsb), argument fungsi, kembalian nilai sebuah fungsi (return value). Topik first-class-citizen berhubungan erat dengan higher order function. Pengertian Higher order function sendiri adalah function yang minimal bisa meletakan fungsi lain pada parameternya atau mengembalikan fungsi lain pada return value-nya.

Berikut ini adalah contoh higher order function menggunakan built-in function filter. Pada contoh code dibawah ini, kita memanfaatkan built-in function filter untuk memfilter data list string yang berawalan dengan huruf b saja. Kemudian hasil filter tersebut ditampung pada variabel baru.

Perhatikan baris ke 3, pada baris ke 3 kita membuat variabel pred, dimana variabel pred adalah sebuah variabel yang menampung value sebuah function. Sehingga fungsi yang ditampung pada variabel pred inilah yang bisa kita sebut first class citizen function.

Kemudian perhatikan baris ke 5, pada baris ke 5 kita menggunakan built-in function filter. Fungsi filter ini memiliki satu parameter yaitu sebuah fungsi. Sehingga fungsi filter ini bisa kita sebut dengan higher order function. Berikut adalah signature dari fungsi filter. Pada signature tersebut parameter satu fungsi dengan parameter generic T dan return type Boolean.

Signature fungsi filter

Lambda expressions dan anonymous function

Contoh code yang sebelumnya kita sudah buat sebenarnya bisa kita sederhanakan lagi menggunakan lambda expressions maupun anonymous function.

Jika anda perlu menentukan tipe parameter dan tipe return-nya secara eksplisit, anda bisa menggunakan anonymous function.

fungsi filter dengan anonymous function

Anonymous function sebenarnya mirip seperti fungsi pada umumnya, bisa diperhatikan dia tetap menggunakan keyword fun. Hanya saja dia tidak memiliki nama.

Namun jika anda tidak perlu menentukan tipe parameter dan tipe return-nya secara eksplisit, anda bisa menggunakan lambda expressions seperti dibawah ini.

fungsi filter dengan lambda expressions.

Sifat dari lambda expression pada Kotlin:

  • Lambda expressions selalu dibuka dan ditutup dengan curly bracket( { dan }).
  • tipe data parameternya opsional .
  • function body berada setelah tanda ->.

Extension functions

Kotlin memberikan keleluasaan kepada kita untuk memberikan fungsionalitas baru pada sebuah class tanpa perlu membuatkan sub class baru dari class tersebut. Sebagai contoh ketika kita perlu membuatkan fungsi baru pada sebuah class yang berasal dari third party library. Hal ini bisa kita solve dengan extension function.

Contoh pertama dibawah ini ketika kita ingin membuatkan satu extension function logging sederhana pada variabel-variabel yang kita buat dengan tipe data string.

Contoh extension function pada String

Contoh kedua dibawah ini kita membuatkan satu extension function untuk mengalikan semua variabel dengan tipe data Int dengan 10. Extension function tersebut kita beri nama mulByTen.

Contoh extension function pada Int

Pada contoh ketiga ini kita akan membuat extension function pada class HttpServer yang kita anggap saja adalah class yang berasal dari third party library.

Class HttpServer

Pada class HttpServer diatas kita menambahkan extension function log. Ketika misalnya kita membutuhkan sebuah fungsionalitas log pada class tersebut. Kita tidak perlu membuatkan sub class baru untuk class HttpServer yang kita bayangkan class tersebut berasal dari third party library.

Scope functions

Kita sudah membahas sedikit pengertian scope function pada bagian awal artikel ini. Scope function adalah built in function yang disediakan oleh Kotlin yang berguna untuk mengeksekusi suatu block code dalam konteks sebuah objek. Untuk memahami scope function pada Kotlin, anda perlu memahami topik seperti extensions function pada Kotlin, higher order function dan anonymous function/lambda yang sudah kita bahas sebelumnya.

Total ada 5 function yang termasuk scope function pada Kotlin. let , run , with , apply , dan also . Lima scope function tersebut dibagi menjadi 2 kategori. Yaitu kategori mutation function, apply dan also . Kemudian kategori transformation function, let , run , dan with . Kelima function tersebut hampir mirip secara penggunaan, hanya saja kita harus tau kapan dan dimana menggunakan masing-masing dari kelima function tersebut.

Context object, this dan it

this dan it adalah context object yang tersedia didalam lambda function pada setiap scope function. Context object this dan it bisa kita gunakan sebagai pengganti nama objek aslinya. Setiap scope function menggunakan salah satu dari this dan it untuk mengakses context object-nya.

this = T.()adalah lambda receiver dan berada didalam lambda function yang berada pada extension function run, with, dan apply. Sedangkan it = (T)adalah lambda argument dan berada didalam lambda function yang berada pada extension function let dan also .

apply

apply adalah scope function yang masuk kategori mutation function. Context object yang tersedia adalah this . Return value dari apply adalah objek dia sendiri. Sehingga apply biasanya digunakan untuk menginisialisasi sebuah objek atau membuat class builder.

Signature:

fun <T> T.apply(block: T.() -> Unit): T

Jika kita perlu menggunakan keyword this pada setiap property class-nya kita bisa tuliskan seperti dibawah ini.

apply dan data class dengan explicit this

Namun jika kita tidak perlu menuliskannya, maka keyword this bisa kita kita omit dan ditulis seperti dibawah ini.

apply dan data class tanpa explicit this

Kita juga bisa memanfaatkan apply untuk mengimplementasikan builder pattern.

apply dan builder pattern

also

also adalah scope function yang masuk kategori mutation function. Context object yang tersedia adalah it. Return value dari also adalah objek dia sendiri. also biasannya digunakan ketika kita butuh akses terhadap object reference-nya itu sendiri, bukan ketika butuh akses terhadap property atau method dari objek tersebut. Atau kita hanya butuh sekedar logic sederhana misalnya untuk logging value pada flow code yang berjalan.

Signature:

fun <T> T.also(block: (T) -> Unit): T
menggunakan also untuk proses logging

Return value dari also adalah objek dia sendiri. Sehingga kita bisa menggunakan also untuk menginisialisasi sebuah objek seperti kita melakukannya ketika menggunakan apply. Hanya saja code yang ditulis menjadi lebih verbose, karena secara eksplisit kita menuliskan context object it .

also untuk inisialisasi objek

let

let adalah scope function yang masuk kategori transformation function. Context object yang tersedia adalah it. Return value dari let adalah result dari lambda dimasukan kedalam scope function-nya.

Signature:

fun <T, R> T.let(block: (T) -> R): R

Berikut contoh penggunaan let untuk melakukan transformasi objek dengan tipe data tertentu ke objek dengan tipe data lain.

let

Pada contoh diatas kita menggunakan let untuk proses transformasi variabel dari tipe data string ke tipe data bytes. Jika tidak menggunakan let mungkin kita perlu membuat variabel baru seperti dibawah ini.

tanpa let

Atau kita bisa menyederhanakan lagi code let dengan menggunakan method reference, dengan catatan fungsi code block hanya ada satu fungsi dengan parameter it . Kita bisa lakukan seperti code dibawah ini.

let dengan method reference

run

run adalah scope function yang masuk kategori transformation function. Context object yang tersedia adalah this. Return value dari run adalah result dari lambda dimasukan kedalam scope function-nya. run tersedia dalam dua jenis, yaitu run sebagai extension function dan run sebagai non extension function. run hampir sama dengan let hanya saja run menggunakan this sebagai context object-nya.

run sebagai extension function

Signature:

fun <T, R> T.run(block: T.() -> R): R
run sebagai extension function

run berguna ketika lambda yang kita provide terdapat inisialisasi objek dan komputasi yang menghasilkan return value seperti pada code diatas.

run sebagai non extension function

Signature:

fun <R> run(block: () -> R): R
run sebagai non extension function

run sebagai non extension function berguna ketika kita membutuhkan beberapa statement sekaligus yang menghasilkan sebuah value.

with

with adalah scope function yang masuk kategori transformation function. By default dia adalah non extension function. Context object yang tersedia adalah this tetapi context object-nya diperlakukan sebagai argument. Return value dari run adalah result dari lambda dimasukan kedalam scope function-nya.

Signature:

fun <T, R> with(receiver: T, block: T.() -> R): R
with

with berguna ketika kita ingin mengumpulkan pemanggilan function/method sebuah object kemudian mendapatkan value dari proses pemanggilan beberapa function/method dari object tersebut. Pada contoh diatas kita mengirim object strikerEureka kedalam fungsi with . Kemudian kita memanggil method kill dua kali, kemudian kita memanggil fungsi killCount untuk mendapatkan total jumlah victims dari strikerEureka .

Tabel perbedaan dari setiap scope function

https://kotlinlang.org/docs/reference/scope-functions.html

Kesimpulan

Kita sudah membahas apa itu scope function pada artikel ini. Karena secara penggunaan setiap scope function tersebut hampir mirip satu sama lain, ada baiknya kita lebih sering memanfaatkannya. Supaya kita bisa belajar dan lebih paham lagi kapan dan dimana menggunakannya.

--

--