Kotlin dari Dasar: Lebih Menyenangkan Dengan Fungsi
() translation by (you can also view the original English article)
Kotlin adalah bahasa pemrograman modern yang mengkompilasi ke bytecode Java. Bahasa pemrograman ini gratis dan open source, dan menjanjikan untuk membuat coding untuk Android semakin menyenangkan.
Pada Artikel sebelumnya, Anda belajar tentang paket dan fungsi dasar di Kotlin. Fungsi berada pada jantung di Kotlin, jadi pada postingan ini kita akan melihat lebih dekat tentang hal itu. Kita akan mengeksplorasi jenis fungsi berikut di Kotlin:
- Top-Level Functions
- Lambda expressions atau Literal Function
- Anonymous Functions
- Local atau Nested Functions
- Infix Functions
- Member Functions
Anda akan kagum pada semua hal keren yang dapat Anda lakukan dengan fungsi di Kotlin!
1. Top-Level Functions
Top-Level Functions adalah fungsi di dalam paket Kotlin yang didefinisikan di luar class, objek, atau interface apa pun. Ini berarti bahwa mereka adalah fungsi yang Anda panggil secara langsung, tanpa perlu membuat objek atau memanggil kelas apapun.
Jika Anda seorang Java coder, Anda tahu bahwa kita biasanya membuat metode utility static di dalam kelas helper. Kelas helper ini tidak benar-benar melakukan apa pun-mereka tidak memiliki kondisi atau instance methods, dan mereka hanya bertindak sebagai wadah untuk metode statis. Contohnya adalah kelas Collections
pada paket java.util
dengan metode statisnya.
Top-level functions di Kotlin dapat digunakan sebagai pengganti metode static utility di kelas helper yang kita buat di Java. Mari kita lihat bagaimana mendefinisikan Top-level functions pada Kotlin.
1 |
package com.chikekotlin.projectx.utils |
2 |
|
3 |
fun checkUserStatus(): String { |
4 |
return "online" |
5 |
}
|
Pada kode di atas, kita mendefinisikan sebuah paket com.chikekotlin.projectx.utils
di dalam file bernama UserUtils.kt dan juga mendefinisikan sebuah top-level utility function bernama checkUserStatus()
di dalam paket dan file yg sama. Untuk singkatnya, fungsi yang sangat sederhana ini mengembalikan string "online".
Hal berikutnya yang akan kita lakukan adalah menggunakan fungsi utilitas ini pada paket atau file lain.
1 |
package com.chikekotlin.projectx.users |
2 |
|
3 |
import com.chikekotlin.projectx.utils.checkUserStatus |
4 |
|
5 |
if (checkUserStatus() == "online") { |
6 |
// do something
|
7 |
}
|
Pada kode sebelumnya, kita mengimpor fungsinya ke paket lain dan kemudian mengeksekusinya! Seperti yang Anda lihat, kita tidak perlu membuat objek atau referensi kelas untuk memanggil fungsi ini.
Java Interoperability
Mengingat Java tidak mendukung top-level functions, kompiler Kotlin di balik layar akan menciptakan kelas Java, dan top-level functions akan dikonversi ke metode statis. Dalam hal ini, kelas Java yang dihasilkan adalah UserUtilsKt
dengan metode statis checkUserStatus()
.
1 |
/* Java */
|
2 |
package com.chikekotlin.projectx.utils |
3 |
|
4 |
public class UserUtilsKt { |
5 |
|
6 |
public static String checkUserStatus() { |
7 |
return "online"; |
8 |
}
|
9 |
}
|
Ini berarti bahwa pemanggil Java hanya bisa memanggil metode tersebut dengan mereferensikan kelas yang dihasilkannya, sama seperti metode statis lainnya.
1 |
/* Java */
|
2 |
import com.chikekotlin.projectx.utils.UserUtilsKt |
3 |
|
4 |
...
|
5 |
|
6 |
UserUtilsKt.checkUserStatus() |
Perhatikan bahwa kita dapat mengubah nama kelas Java yang dihasilkan oleh kompiler Kotlin dengan menggunakan keterangan @JvmName
.
1 |
@file:JvmName("UserUtils") |
2 |
package com.chikekotlin.projectx.utils |
3 |
|
4 |
fun checkUserStatus(): String { |
5 |
return "online" |
6 |
}
|
Pada kode di atas, kita menerapkan keterangan @JvmName
dan menentukan nama kelas UserUtils
untuk file yang akan dihasilkan. Perhatikan juga bahwa keterangan ini diletakkan pada awal file Kotlin, sebelum definisi paket.
Hal ini bisa dilihat dari Java seperti ini:
1 |
/* Java */
|
2 |
import com.chikekotlin.projectx.utils.UserUtils |
3 |
|
4 |
...
|
5 |
|
6 |
UserUtils.checkUserStatus() |
2. Lambda Expressions
Lambda expressions (atau function literals) juga tidak terikat pada entitas seperti class, objek, atau interface. Mereka dapat dilewatkan sebagai argumen ke fungsi lain yang disebut fungsi dengan higher-order functions (kita akan membahasnya lebih lanjut di posting berikutnya). Lambda expressions hanya mewakili blok fungsi, dan menggunakannya mengurangi kekacauan dalam kode kita.
Jika Anda seorang Java coder, Anda tahu bahwa Java 8 atau versi di atasnya memberikan dukungan untuk Lambda expressions. Untuk menggunakan Lambda expressions dalam sebuah proyek yang mendukung Java versi lama seperti Java 7, 6, atau 5, kita bisa menggunakan library Retrolambda.
Salah satu hal yang menakjubkan dari Kotlin adalah didukungnya Lambda expressions. Karena lambda tidak didukung di Java 6 atau 7, untuk Kotlin agar berkoordinasi dengan hal tersebut, menciptakan kelas anonim Java di balik layar. Tapi perhatikan bahwa membuat lambda expression di Kotlin sangat berbeda dibanding di Java.
Berikut karakteristik lambda expresssion di Kotlin:
- Harus diapit oleh kurung kurawal
{}
. - Tidak memiliki kata kunci
fun
. - Tidak ada pengubah akses (private, public atau protected) karena tidak termasuk class, objeck, atau interface apa pun
- Tidak ada nama fungsi. Dengan kata lain, anonim.
- Tidak ada tipe return yang ditentukan karena akan diambil oleh compiler.
- Parameter tidak diapit oleh tanda kurung
()
.
Dan, terlebih lagi, kita bisa menetapkan lambda expression ke variabel dan kemudian mengeksekusinya.
Membuat Lambda Expressions
Sekarang mari kita lihat beberapa contoh lambda expression. Pada kode di bawah ini, kita membuat lambda expression tanpa parameter dan menetapkannya sebagai variabel message
. Lalu kita mengeksekusinya dengan memanggil fungsi message()
.
1 |
val message = { println("Hey, Kotlin is really cool!") } |
2 |
message() // "Hey, Kotlin is really cool!" |
Lihat juga melihat bagaimana memasukkan parameter pada lambda expression.
1 |
val message = { myString: String -> println(myString) } |
2 |
message("I love Kotlin") // "I love Kotlin" |
3 |
message("How far?") // "How far?" |
Pada kode di atas, kita membuat lambda expression dengan parameter myString
, beserta tipe parameternya yaitu String
. Seperti yang bisa Anda lihat, di depan tipe parameter, ada anak panah: ini mengacu pada isi lambda. Dengan kata lain, panah ini memisahkan daftar parameter dari isi lambda. Singkatnya, kita benar-benar bisa mengabaikan tipe parameter (sudah diatur oleh kompilator).
1 |
val message = { myString -> println(myString) } // will still compile |
Untuk dapat memiliki banyak parameter, kita bisa memisahkannya dengan koma. Dan ingat, kita tidak membungkus daftar parameter dalam tanda kurung seperti di Java.
1 |
val addNumbers = { number1: Int, number2: Int -> |
2 |
println("Adding $number1 and $number2") |
3 |
val result = number1 + number2 |
4 |
println("The result is $result") |
5 |
}
|
6 |
addNumbers(1, 3) |
Namun, perhatikan bahwa jika jenis parameter tidak dapat disimpulkan, mereka harus ditentukan secara eksplisit (seperti pada contoh ini), jika tidak, kode tersebut tidak akan dikompilasi.
1 |
Adding 1 and 3 |
2 |
The result is 4 |
Melewatkan Lambda ke fungsi
Kita bisa melewati lambda expression sebagai parameter ke fungsi: ini disebut "higher-order functions", karena fungsi di dalam fungsi. Jenis fungsi ini dapat menerima fungsi lambda atau anonim sebagai parameter: misalnya, fungsi collection last()
.
Dalam kode di bawah ini, kita melewatkan sebuah lambda expression ke fungsi last()
. (Jika Anda menginginkan penjelasan tentang collection di Kotlin, kunjungi tutorial ke tiga dalam seri ini) Seperti namanya, ia mengembalikan elemen terakhir dalam daftar. Fungsi last()
menerima lambda expression sebagai parameter, dan ekspresi ini pada gilirannya mengambil satu argumen tipe String
. Isi fungsinya berfungsi sebagai predikat untuk mencari di dalam subkumpulan elemen dalam collection Itu berarti bahwa lambda expression akan menentukan elemen collection mana yang akan dipertimbangkan saat mencari yang terakhir.
1 |
val stringList: List<String> = listOf("in", "the", "club") |
2 |
print(stringList.last()) // will print "club" |
3 |
print(stringList.last({ s: String -> s.length == 3})) // will print "the" |
Mari kita lihat bagaimana membuat kode di atas lebih mudah dibaca.
1 |
stringList.last { s: String -> s.length == 3 } // will also compile and print "the" |
Kompiler Kotlin memungkinkan kita untuk menghapus tanda kurung fungsi jika argumen terakhir dalam fungsinya adalah lambda expression. Seperti yang dapat Anda amati dalam kode di atas, kita dapat melakukan ini karena argumen terakhir dan satu-satunya yang disampaikan ke fungsi last()
adalah lambda expression.
Selanjutnya, kita bisa membuatnya lebih ringkas dengan cara menghapus tipe parameternya.
1 |
stringList.last { s -> s.length == 3 } // will also compile print "the" |
Kita tidak perlu menentukan jenis parameter secara eksplisit, karena tipe parameternya selalu sama dengan tipe elemen collection. Dalam kode di atas, kita panggil last
pada kumpulan collection dari objek String
, jadi compiler Kotlin akan tahu bahwa parameternya juga akan menjadi tipe String
.
Nama Argumen it
Kita bahkan dapat menyederhanakan lambda expression lebih jauh lagi dengan mengganti argumen lambda expression yang dihasilkan secara otomatis dengan nama argumen default yaitu it
.
1 |
stringList.last { it.length == 3 } |
Argumen it
secara otomatis dihasilkan karena last
dapat menerima lambda expression atau anonym function (kita akan melakukannya) dengan hanya satu argumen, dan jenisnya dapat disimpulkan oleh kompilator.
Local Return pada Lambda Expressions
Mari kita mulai dengan sebuah contoh. Dalam kode di bawah ini, kita melewatkan sebuah lambda expression ke fungsi foreach()
yang dipanggil di collection intList
. Fungsi ini akan melewati collection dan mengeksekusi lambda pada setiap elemen dalam daftar. Jika ada elemen yang habis dibagi 2, ia akan berhenti dan kembali dari lambda.
1 |
fun surroundingFunction() { |
2 |
val intList = listOf(1, 2, 3, 4, 5) |
3 |
intList.forEach { |
4 |
if (it % 2 == 0) { |
5 |
return
|
6 |
}
|
7 |
}
|
8 |
println("End of surroundingFunction()") |
9 |
}
|
10 |
|
11 |
surroundingFunction() // nothing happened |
Menjalankan kode di atas mungkin tidak memberi Anda hasil yang Anda harapkan. Hal ini karena return statement tersebut tidak akan kembali dari lambda melainkan dari fungsi surroundingFunction()
! Ini berarti statement kode terakhir di surroundingFunction()
tidak akan dieksekusi.
1 |
// ...
|
2 |
println("End of surroundingFunction()") // This won't execute |
3 |
// ...
|
Untuk memperbaiki masalah ini, kita perlu memberitahukannya secara eksplisit fungsi mana yang akan di return menggunakan label atau name tag.
1 |
fun surroundingFunction() { |
2 |
val intList = listOf(1, 2, 3, 4, 5) |
3 |
intList.forEach { |
4 |
if (it % 2 == 0) { |
5 |
return@forEach |
6 |
}
|
7 |
}
|
8 |
println("End of surroundingFunction()") // Now, it will execute |
9 |
}
|
10 |
|
11 |
surroundingFunction() // print "End of surroundingFunction()" |
Dalam kode yang diperbarui di atas, kita menentukan tag default @forEach
setelah kata return
di dalam lambda. Kita sekarang menginstruksikan compiler untuk melakukan return dari lambda, bukan fungsi surroundingFunction()
. Sekarang, statement terakhir dari surroundingFunction()
akan dieksekusi.
Perhatikan bahwa kita juga dapat menentukan label atau name tag kita sendiri.
1 |
// ...
|
2 |
intList.forEach myLabel@ { |
3 |
if (it % 2 == 0) { |
4 |
return@myLabel |
5 |
// ...
|
Pada kode di atas, kita mendefinisikan label kita yang bernama myLabel@
dan menentukannya sebagai return
. label @forEach
dihasilkan oleh kompilator untuk fungsi forEach
tidak lagi tersedia karena kita sudah mendefinisikannya sendiri.
Namun, Anda akan segera melihat bagaimana masalah local return ini dapat dipecahkan tanpa label saat kita membahas Fungsi Anonymous di Kotlin nanti.
3. Member Functions
Jenis fungsi ini didefinisikan di dalam class, objek, atau interface. Dengan menggunakan member function kita dapat membuat modularize program kita lebih jauh. Mari kita lihat bagaimana cara membuat member function.
1 |
class Circle { |
2 |
|
3 |
fun calculateArea(radius: Double): Double { |
4 |
require(radius > 0, { "Radius must be greater than 0" }) |
5 |
return Math.PI * Math.pow(radius, 2.0) |
6 |
}
|
7 |
}
|
Kode ini menunjukkan kelas Circle
(kita akan membahas kelas Kotlin di posting selanjutnya) yang memiliki member function calculateArea()
. Fungsi ini memiliki parameter radius
untuk menghitung luas lingkaran.
Untuk memanggil member function, kita menggunakan nama kelas atau contoh objek dengan titik, diikuti dengan nama fungsi, juga argumen jika perlu.
1 |
val circle = Circle() |
2 |
print(circle.calculateArea(4.5)) // will print "63.61725123519331" |
4. Fungsi Anonymous
Fungsi Anonymous adalah cara lain untuk menentukan blok kode yang dapat dilewatkan pada sebuah fungsi. Hal ini tidak terikat dengan identifier apapun. Berikut adalah karakteristik anonym function di Kotlin:
- Tidak memiliki nama
- Dibuat dengan keyword
fun
- Terdapat isi dari sebuah fungsi
1 |
val stringList: List<String> = listOf("in", "the", "club") |
2 |
print(stringList.last{ it.length == 3}) // will print "the" |
Karena kita akan melewatkan lambda ke fungsi last()
di atas, function above, kita tidak bisa secara gamblang menggunakan tipe return. Untuk dapat melakukan hal itu, kita perlu menggunakan fungsi anonymous sebagai gantinya.
1 |
val strLenThree = stringList.last( fun(string): Boolean { |
2 |
return string.length == 3 |
3 |
})
|
4 |
print(strLenThree) // will print "the" |
Pada kode di atas, kita telah mengganti lambda expression dengan fungsi anonymous karena kita ingin menggunakan tipe return secara eksplisit.
Menjelang akhir bagian lambda dalam tutorial ini, kita menggunakan label untuk menentukan fungsi mana yang akan di return. Menggunakan anonym function dan bukan lambda di dalam fungsi forEach()
mengatasi permasalahan dengan lebih mudah. return expression dari fungsi anonymous dan bukan dari yang di sekitarnya, yang dalam hal ini adalah fungsi surroundingFunction()
.
1 |
fun surroundingFunction() { |
2 |
val intList = listOf(1, 2, 3, 4, 5) |
3 |
intList.forEach ( fun(number) { |
4 |
if (number % 2 == 0) { |
5 |
return
|
6 |
}
|
7 |
})
|
8 |
println("End of surroundingFunction()") // statement executed |
9 |
}
|
10 |
|
11 |
surroundingFunction() // will print "End of surroundingFunction()" |
5. Local atau Nested Functions
Untuk mengambil modulisasi program lebih jauh, Kotlin memberi kita fungsi lokal - juga dikenal sebagai fungsi nested. Fungsi lokal adalah fungsi yang dideklarasikan di dalam fungsi lain.
1 |
fun printCircumferenceAndArea(radius: Double): Unit { |
2 |
|
3 |
fun calCircumference(radius: Double): Double = (2 * Math.PI) * radius |
4 |
val circumference = "%.2f".format(calCircumference(radius)) |
5 |
|
6 |
fun calArea(radius: Double): Double = (Math.PI) * Math.pow(radius, 2.0) |
7 |
val area = "%.2f".format(calArea(radius)) |
8 |
|
9 |
print("The circle circumference of $radius radius is $circumference and area is $area") |
10 |
}
|
11 |
|
12 |
printCircumferenceAndArea(3.0) // The circle circumference of 3.0 radius is 18.85 and area is 28.27 |
Seperti yang dapat Anda amati dalam kode di atas, kita memiliki dua fungsi: calCircumference()
dan calArea()
nested di dalam fungsi printCircumferenceAndAread()
. Fungsi Nested bisa dipanggil hanya dari dalam fungsi, bukan di luar. Sekali lagi, penggunaan fungsi nested membuat program kita lebih modular dan rapi.
Kita dapat membuat fungsi lokal kita lebih ringkas dengan tidak mengirimkan parameter secara eksplisit. Hal ini dimungkinkan karena fungsi lokal memiliki akses ke semua parameter dan variabel fungsi. Mari kita lihat kode berikut:
1 |
fun printCircumferenceAndArea(radius: Double): Unit { |
2 |
|
3 |
fun calCircumference(): Double = (2 * Math.PI) * radius |
4 |
val circumference = "%.2f".format(calCircumference()) |
5 |
|
6 |
fun calArea(): Double = (Math.PI) * Math.pow(radius, 2.0) |
7 |
val area = "%.2f".format(calArea()) |
8 |
// ...
|
9 |
}
|
Seperti yang Anda lihat, kode yang diperbarui ini terlihat lebih mudah dibaca dan mengurangi kekacauan yang kita alami sebelumnya. Meskipun fungsi dalam contoh ini diberikan kecil, dalam fungsi lebih besar yang dapat dipecah menjadi fungsi nested yang lebih kecil, fitur ini sangat berguna.
6. Infix Functions
Fungsi infix
memungkinkan kita dengan mudah untuk memanggil satu argumen member function atau fungsi ekstensi. Selain fungsi menjadi satu argumen, Anda juga harus menentukan fungsi menggunakan modifier infix
. Untuk membuat fungsi infix, dua parameter dilibatkan. Parameter pertama adalah target objek, sedangkan parameter kedua hanyalah satu parameter yang dilewatkan ke fungsi.
Membuat Infix Member Function
Mari kita lihat cara membuat fungsi infix pada kelas. Pada contoh kode di bawah ini, kita membuat kelas tidak tetap Student
dengan instance kotlinScore
. Kita membuat fungsi infix dengan menggunakan modifier infix
sebelum keyword fun
. Seperti yang bisa Anda lihat di bawah, kita membuat fungsi infix addKotlinScore()
yang menggunakan skor dan menambah nilai instance kotlinScore
.
1 |
class Student { |
2 |
var kotlinScore = 0.0 |
3 |
|
4 |
infix fun addKotlinScore(score: Double): Unit { |
5 |
this.kotlinScore = kotlinScore + score |
6 |
}
|
7 |
}
|
Memanggil Fungsi Infix
Mari kita lihat bagaimana cara memanggil fungsi infix yang telah kita buat. Untuk memanggil fungsi infix pada Kotlin, kita tidak perlu menggunakan notasi titik, dan kita tidak perlu membungkus parameter dengan tanda kurung.
1 |
val student = Student() |
2 |
student addKotlinScore 95.00 |
3 |
print(student.kotlinScore) // will print "95.0" |
Pada kode di atas, kita memanggil fungsi infix, target objeknya adalah student
, dan mengalikan dengan 95.00
adalah parameter yang dilewatkan pada fungsi.
Menggunakan fungsi infix membuat kode kita lebih ekspresif dan jernih dari pada cara biasanya. Hal ini sangat berguna saat membuat test unit di Kotlin (kita akan membahas pengujian di Kotlin di posting berikutnya).
1 |
"Chike" should startWith("ch") |
2 |
myList should contain(myElement) |
3 |
"Chike" should haveLength(5) |
4 |
myMap should haveKey(myKey) |
Fungsi Infix to
Di Kotlin, kita bisa membuat instance Pair
lebih ringkas dengan menggunakan fungsi infix to
dibandingkan constructor Pair
. (di balik layar, to
juga membuat sebuah instance Pair
) Ingat bahwa fungsi to
juga merupakan fungsi extension (kita akan membahasnya lebih lanjut di posting berikutnya).
1 |
public infix fun <A, B> A.to(that: B): Pair<A, B> = Pair(this, that) |
Sekarang bandingkan instance Pair
menggunakan fungsi to
dan constructor Pair
, yang melakukan operasi yang sama, dan lihatlah mana yang lebih baik.
1 |
val nigeriaCallingCodePair = 234 to "Nigeria" |
2 |
val nigeriaCallingCodePair2 = Pair(234, "Nigeria") // Same as above |
Seperti yang bisa anda lihat pada kode di atas, dengan menggunakan fungsi to
lebih singkat daripada menggunakan constructor Pair
untuk membuat sebuah instance Pair
. Ingat bahwa menggunakan fungsi infix to
, 234
adalah target objek dan String
"Nigeria" adalah parameter yang dilewatkan ke fungsi. Apalagi, perlu dicatat bahwa kita juga bisa melakukan ini untuk membuat tipe Pair
:
1 |
val nigeriaCallingCodePair3 = 234.to("Nigeria") // same as using 234 to "Nigeria" |
Pada postingan Ranges and Collections, kita membuat map collection di Kotlin dengan memberikan beberapa daftar - nilai pertama menjadi kuncinya, dan yang kedua nilainya. Mari kita juga membandingkan pembuatan peta dengan menggunakan keduanya. Fungsi infix to
dan constructor Pair
untuk membuat sebuah instance pair.
1 |
val callingCodesMap: Map<Int, String> = mapOf(234 to "Nigeria", 1 to "USA", 233 to "Ghana") |
Pada kode di atas, kita membuat daftar yang dipisahkan koma bertipe Pair
menggunakan fungsi to
infix dan dilewatkan ke fungsi mapOf()
. Kita juga bisa membuat peta yang sama dengan langsung menggunakan constructor Pair
untuk setiap instance pair.
1 |
val callingCodesPairMap: Map<Int, String> = mapOf(Pair(234, "Nigeria"), Pair(1, "USA"), Pair(233, "Ghana")) |
Seperti yang bisa Anda lihat, fungsi infix to
lebih ringkas daripada menggunakan constructor Pair
.
Kesimpulan
Dalam tutorial ini, Anda belajar tentang beberapa hal yang dapat Anda lakukan dengan fungsi di Kotlin. Yaitu:
- Top-Level Function
- Lambda Expressions atau Literal Function
- Member Function
- Anonymous Function
- Local atau Nested Function
- Infix Function
Tapi bukan itu saja! Masih banyak yang harus dipelajari tentang fungsi di Kotlin. Jadi, di posting berikutnya, Anda akan mempelajari beberapa penggunaan fungsi lanjutan, seperti extension dan higher-order functions. Sampai jumpa lagi!
Untuk mempelajari lebih lanjut tentang bahasa Kotlin, kunjungi Dokumentasi Kotlin. Atau lihat beberapa posting pengembangan aplikasi Android kami yang lain di Envato Tuts+!
- Android SDKCara menggunakan Google Cloud Vision API dalam Aplikasi AndroidAshraff Hathibelagal
- JavaAndroid Design Patterns: Observer-PatternChike Mgbemena
- Android SDKMenambahkan Animasi Berbasis Fisika pada Android AppsAshraff Hathibelagal
- Android SDKAndroid O: Verifikasi Nomor Telepon dengan SMS TokenChike Mgbemena
- Android SDKApakah itu Android Instant Apps?Jessica Thornsby