Skip to main content

Closure

Closure adalah kemampuan sebuah function, lambda, atau anonymous function untuk mengakses dan menggunakan variable yang berada di luar function tersebut tetapi masih berada dalam scope yang dapat diakses.

Dengan Closure, sebuah function dapat mengingat dan memodifikasi data dari scope di sekitarnya.

Contoh Sederhana Closure

Perhatikan contoh berikut:

fun main() {

var counter = 0

fun functionIncrement() {
counter++
println("Function Increment")
}

functionIncrement()
functionIncrement()
functionIncrement()
functionIncrement()
functionIncrement()
functionIncrement()

println(counter)
}

Output:

Function Increment
Function Increment
Function Increment
Function Increment
Function Increment
Function Increment
6

Memahami Closure pada Contoh

Pada function main() terdapat variable:

var counter = 0

Kemudian kita membuat function di dalam main():

fun functionIncrement() {
counter++
println("Function Increment")
}

Perhatikan bahwa function functionIncrement() tidak memiliki parameter bernama counter.

Namun, function tersebut tetap dapat mengakses:

counter

yang dibuat di luar function.

Inilah salah satu contoh Closure.

Function Mengakses Variable di Outer Scope

Secara sederhana, struktur kode tersebut dapat digambarkan:

main()

├── counter = 0

└── functionIncrement()

└── mengakses counter

Function functionIncrement() berada di dalam scope main() sehingga dapat mengakses variable counter.

Mengubah Nilai Variable dari Closure

Closure tidak hanya dapat membaca variable dari outer scope, tetapi juga dapat mengubah nilainya jika variable tersebut dapat dimodifikasi.

Pada contoh:

var counter = 0

digunakan var, sehingga nilainya dapat diubah.

Kemudian:

counter++

menambahkan nilai counter sebanyak 1.

Ketika function dipanggil:

functionIncrement()

nilai counter berubah.

Prosesnya:

counter = 0

functionIncrement()
counter = 1

functionIncrement()
counter = 2

functionIncrement()
counter = 3

Dan seterusnya.

Mengapa Menggunakan var?

Perhatikan variable:

var counter = 0

Kita menggunakan var karena nilainya akan berubah.

Contoh:

counter++

Jika menggunakan val:

val counter = 0

maka kita tidak dapat melakukan:

counter++

karena val tidak dapat diubah setelah diberikan nilai.

Contoh Closure dengan Lambda

Closure juga dapat terjadi pada Lambda Expression.

Contoh:

fun main() {

var counter = 0

val increment = {
counter++
}

increment()
increment()
increment()

println(counter)
}

Output:

3

Lambda:

val increment = {
counter++
}

dapat mengakses variable:

counter

yang berada di luar lambda.

Lambda tersebut membentuk closure terhadap variable counter.

Closure dengan Lambda dan Parameter

Closure juga dapat digunakan bersama parameter.

Contoh:

fun main() {

var total = 0

val tambah = { value: Int ->
total += value
}

tambah(10)
tambah(20)
tambah(30)

println(total)
}

Output:

60

Lambda tambah dapat mengakses variable total yang berada di luar lambda.

Setiap kali lambda dipanggil, nilai total akan berubah.

Prosesnya:

total = 0

tambah(10)
total = 10

tambah(20)
total = 30

tambah(30)
total = 60

Closure dengan Anonymous Function

Closure juga dapat digunakan pada Anonymous Function.

Contoh:

fun main() {

var counter = 0

val increment = fun() {
counter++
}

increment()
increment()
increment()

println(counter)
}

Output:

3

Anonymous Function:

fun() {
counter++
}

dapat mengakses variable:

counter

yang berada di luar function tersebut.

Closure pada Higher-Order Function

Closure juga sering digunakan bersama Higher-Order Function.

Contoh:

fun createCounter(): () -> Int {

var counter = 0

return {
counter++
counter
}
}

Function createCounter() mengembalikan sebuah lambda:

() -> Int

Lambda tersebut menggunakan variable:

counter

yang dibuat di dalam createCounter().

Contoh penggunaannya:

fun main() {

val counter = createCounter()

println(counter())
println(counter())
println(counter())
}

Output:

1
2
3

Bagaimana Closure Bekerja?

Pada contoh:

fun createCounter(): () -> Int {

var counter = 0

return {
counter++
counter
}
}

Lambda yang dikembalikan masih dapat mengakses:

counter

meskipun function createCounter() sudah selesai dijalankan.

Secara sederhana:

createCounter()

├── counter = 0

└── return lambda

└── mengingat counter

Lambda tersebut mempertahankan akses terhadap data yang dibutuhkannya.

Contoh Membuat Counter

Contoh program lengkap:

fun createCounter(): () -> Int {

var counter = 0

return {
counter++
counter
}
}

fun main() {

val counter = createCounter()

println(counter())
println(counter())
println(counter())
println(counter())
}

Output:

1
2
3
4

Setiap kali counter() dipanggil, variable counter terus bertambah.

Closure dengan Dua Counter

Hal menarik dari Closure adalah setiap pemanggilan createCounter() dapat memiliki state-nya sendiri.

Contoh:

fun createCounter(): () -> Int {

var counter = 0

return {
counter++
counter
}
}

fun main() {

val counterA = createCounter()
val counterB = createCounter()

println(counterA())
println(counterA())
println(counterA())

println(counterB())
println(counterB())
}

Output:

1
2
3
1
2

counterA dan counterB memiliki data counter masing-masing.

Secara sederhana:

counterA
└── counter = 0

counterB
└── counter = 0

Ketika counterA dipanggil, hanya counterA yang berubah.

Ketika counterB dipanggil, hanya counterB yang berubah.

Closure dan State

Closure sering digunakan untuk mempertahankan state.

Contoh:

fun createCounter(): () -> Int {

var counter = 0

return {
counter++
counter
}
}

Variable:

counter

menyimpan state yang dipertahankan oleh lambda.

Setiap pemanggilan:

counter()

mengubah state tersebut.

Kapan Closure Digunakan?

Closure dapat berguna ketika kita membutuhkan function yang dapat mempertahankan atau mengakses data dari scope di sekitarnya.

Contohnya:

  • Membuat counter.
  • Menyimpan state.
  • Membuat function generator.
  • Membuat callback.
  • Membuat Higher-Order Function.
  • Membuat konfigurasi function.
  • Membuat logic yang membutuhkan data dari outer scope.

Contoh sederhana:

fun createGreeting(prefix: String): (String) -> String {

return { name ->
"$prefix $name"
}
}

Penggunaan:

fun main() {

val greeting = createGreeting("Hello")

println(greeting("Ucup"))
println(greeting("Budi"))
}

Output:

Hello Ucup
Hello Budi

Lambda tersebut menggunakan variable prefix dari scope createGreeting().

Gunakan Closure dengan Bijak

Closure merupakan fitur yang powerful, tetapi penggunaannya perlu diperhatikan.

Jika terlalu banyak function atau lambda yang mengubah variable dari outer scope, kode dapat menjadi lebih sulit dipahami.

Contoh:

var total = 0

val tambah = {
total += 10
}

val kurang = {
total -= 5
}

Beberapa lambda dapat mengubah variable yang sama.

Jika logic semakin kompleks, perubahan state dapat menjadi sulit dilacak.

Karena itu, gunakan Closure ketika memang memberikan manfaat terhadap desain program.

Kesimpulan

Closure adalah kemampuan function, lambda, atau anonymous function untuk mengakses variable yang berada di scope luar yang dapat diaksesnya.

Contoh:

fun main() {

var counter = 0

fun functionIncrement() {
counter++
println("Function Increment")
}

functionIncrement()
functionIncrement()
functionIncrement()

println(counter)
}

Function functionIncrement() dapat mengakses:

counter

meskipun counter tidak diberikan sebagai parameter.

Closure juga dapat digunakan pada Lambda:

var counter = 0

val increment = {
counter++
}

dan Anonymous Function:

var counter = 0

val increment = fun() {
counter++
}

Salah satu penggunaan penting Closure adalah mempertahankan state:

fun createCounter(): () -> Int {

var counter = 0

return {
counter++
counter
}
}

Dengan Closure, lambda yang dikembalikan dapat tetap mengakses dan mempertahankan counter.

tip

Closure memungkinkan function, lambda, atau anonymous function menggunakan data dari scope di sekitarnya. Gunakan fitur ini dengan bijak agar state dan alur perubahan data tetap mudah dipahami.