Saya ingin memiliki contoh yang baik untuk setiap fungsi yang dijalankan, biarkan, terapkan, juga, dengan
Saya sudah membaca artikel ini tapi masih kurang contohnya
Saya ingin memiliki contoh yang baik untuk setiap fungsi yang dijalankan, biarkan, terapkan, juga, dengan
Saya sudah membaca artikel ini tapi masih kurang contohnya
Jawaban:
Semua fungsi ini digunakan untuk mengalihkan ruang lingkup fungsi / variabel saat ini. Mereka digunakan untuk menyimpan hal-hal yang menjadi milik bersama di satu tempat (kebanyakan inisialisasi).
Berikut beberapa contohnya:
run
- mengembalikan apa pun yang Anda inginkan dan memeriksa kembali variabel yang digunakannya this
val password: Password = PasswordGenerator().run {
seed = "someString"
hash = {s -> someHash(s)}
hashRepetitions = 1000
generate()
}
Generator kata sandi sekarang dicakup ulang this
dan oleh karena itu kita dapat mengaturnya seed
, hash
dan hashRepetitions
tanpa menggunakan variabel.
generate()
akan mengembalikan contoh Password
.
apply
serupa, tetapi akan mengembalikan this
:
val generator = PasswordGenerator().apply {
seed = "someString"
hash = {s -> someHash(s)}
hashRepetitions = 1000
}
val pasword = generator.generate()
Itu sangat berguna sebagai pengganti pola Builder, dan jika Anda ingin menggunakan kembali konfigurasi tertentu.
let
- Sebagian besar digunakan untuk menghindari pemeriksaan nol, tetapi juga dapat digunakan sebagai pengganti run
. Perbedaannya adalah, itu this
akan tetap sama seperti sebelumnya dan Anda mengakses variabel yang dicakup ulang menggunakan it
:
val fruitBasket = ...
apple?.let {
println("adding a ${it.color} apple!")
fruitBasket.add(it)
}
Kode di atas akan menambahkan apel ke keranjang hanya jika bukan nol. Perhatikan juga bahwa it
sekarang tidak opsional lagi sehingga Anda tidak akan mengalami NullPointerException di sini (alias. Anda tidak perlu menggunakan ?.
untuk mengakses atributnya)
also
- gunakan saat Anda ingin menggunakan apply
, tetapi tidak ingin membayangithis
class FruitBasket {
private var weight = 0
fun addFrom(appleTree: AppleTree) {
val apple = appleTree.pick().also { apple ->
this.weight += apple.weight
add(apple)
}
...
}
...
fun add(fruit: Fruit) = ...
}
Menggunakan di apply
sini akan membuat bayangan this
, jadi itu this.weight
akan mengacu pada apel, dan bukan pada keranjang buah.
Catatan: Saya tanpa malu-malu mengambil contoh dari blog saya
Ada beberapa artikel lagi seperti di sini , dan di sini yang layak untuk dilihat.
Saya pikir itu tergantung pada kapan Anda membutuhkan yang lebih pendek, lebih ringkas dalam beberapa baris, dan untuk menghindari pengecekan pernyataan bercabang atau bersyarat (seperti jika tidak null, maka lakukan ini).
Saya suka grafik sederhana ini, jadi saya menautkannya di sini. Anda bisa melihatnya dari sini seperti yang ditulis oleh Sebastiano Gottardo.
Silakan juga lihat grafik yang menyertai penjelasan saya di bawah ini.
Saya pikir itu sebagai cara bermain peran di dalam blok kode Anda ketika Anda memanggil fungsi-fungsi itu + apakah Anda ingin diri Anda kembali (ke fungsi panggilan berantai, atau diatur ke variabel hasil, dll).
Di atas adalah apa yang saya pikirkan.
Mari kita lihat contoh semuanya di sini
1.) myComputer.apply { }
berarti Anda ingin bertindak sebagai aktor utama (Anda ingin berpikir bahwa Anda adalah komputer), dan Anda ingin kembali (komputer) sehingga Anda dapat melakukannya
var crashedComputer = myComputer.apply {
// you're the computer, you yourself install the apps
// note: installFancyApps is one of methods of computer
installFancyApps()
}.crash()
Yup, Anda sendiri hanya menginstal aplikasi, crash sendiri, dan menyimpan diri Anda sendiri sebagai referensi untuk memungkinkan orang lain melihat dan melakukan sesuatu dengannya.
2.) myComputer.also {}
berarti Anda benar-benar yakin Anda bukan komputer, Anda orang luar yang ingin melakukan sesuatu dengannya, dan juga menginginkan komputer sebagai hasil yang dikembalikan.
var crashedComputer = myComputer.also {
// now your grandpa does something with it
myGrandpa.installVirusOn(it)
}.crash()
3.) with(myComputer) { }
berarti Anda adalah aktor utama (komputer), dan Anda tidak menginginkan diri Anda sebagai hasilnya kembali.
with(myComputer) {
// you're the computer, you yourself install the apps
installFancyApps()
}
4.) myComputer.run { }
berarti Anda adalah aktor utama (komputer), dan Anda tidak menginginkan diri Anda sebagai hasilnya kembali.
myComputer.run {
// you're the computer, you yourself install the apps
installFancyApps()
}
tetapi ini berbeda dengan with { }
dalam arti yang sangat halus bahwa Anda dapat melakukan panggilan berantai run { }
seperti berikut ini
myComputer.run {
installFancyApps()
}.run {
// computer object isn't passed through here. So you cannot call installFancyApps() here again.
println("woop!")
}
Ini karena run {}
adalah fungsi ekstensi, tetapi with { }
tidak. Jadi Anda memanggil run { }
dan this
di dalam blok kode akan direfleksikan ke jenis objek pemanggil. Anda dapat melihat ini untuk penjelasan yang sangat bagus tentang perbedaan antara run {}
dan with {}
.
5.) myComputer.let { }
berarti Anda adalah orang luar yang melihat komputer, dan ingin melakukan sesuatu tentangnya tanpa peduli jika komputer dikembalikan kepada Anda lagi.
myComputer.let {
myGrandpa.installVirusOn(it)
}
Saya cenderung melihat also
dan let
sebagai sesuatu yang eksternal, di luar. Setiap kali Anda mengucapkan dua kata ini, itu seperti Anda mencoba untuk bertindak atas sesuatu. let
instal virus di komputer ini, dan also
hancurkan. Jadi, ini menunjukkan apakah Anda seorang aktor atau bukan.
Untuk bagian hasil, jelas ada. also
menyatakan bahwa itu juga hal lain, jadi Anda masih mempertahankan ketersediaan objek itu sendiri. Jadi itu mengembalikannya sebagai hasil.
Semua hal lain yang terkait dengan this
. Selain iturun/with
jelas tidak tertarik pada return object-self back. Sekarang Anda dapat membedakan semuanya.
Saya pikir terkadang ketika kita menjauh dari 100% pemrograman / contoh berbasis logika, maka kita berada pada posisi yang lebih baik untuk membuat konsep. Tapi itu tergantung kan :)
let, juga, apply, takeIf, takeUnless adalah fungsi ekstensi di Kotlin.
Untuk memahami fungsi ini, Anda harus memahami fungsi Ekstensi dan fungsi Lambda di Kotlin.
Fungsi Ekstensi:
Dengan menggunakan fungsi ekstensi, kita dapat membuat fungsi untuk kelas tanpa harus mewarisi kelas.
Kotlin, mirip dengan C # dan Gosu, memberikan kemampuan untuk memperluas kelas dengan fungsionalitas baru tanpa harus mewarisi dari kelas atau menggunakan jenis pola desain apa pun seperti Dekorator. Ini dilakukan melalui deklarasi khusus yang disebut ekstensi. Kotlin mendukung fungsi ekstensi dan properti ekstensi.
Jadi, untuk menemukan jika hanya angka di String
, Anda dapat membuat metode seperti di bawah ini tanpa mewarisi String
kelas.
fun String.isNumber(): Boolean = this.matches("[0-9]+".toRegex())
Anda dapat menggunakan fungsi ekstensi di atas seperti ini,
val phoneNumber = "8899665544"
println(phoneNumber.isNumber)
yang merupakan cetakan true
.
Fungsi Lambda:
Fungsi Lambda seperti Antarmuka di Java. Namun di Kotlin, fungsi lambda dapat diteruskan sebagai parameter dalam fungsi.
Contoh:
fun String.isNumber(block: () -> Unit): Boolean {
return if (this.matches("[0-9]+".toRegex())) {
block()
true
} else false
}
Anda bisa lihat, blok tersebut adalah fungsi lambda dan dilewatkan sebagai parameter. Anda dapat menggunakan fungsi di atas seperti ini,
val phoneNumber = "8899665544"
println(phoneNumber.isNumber {
println("Block executed")
})
Fungsi di atas akan mencetak seperti ini,
Block executed
true
Saya harap, sekarang Anda mendapat gambaran tentang fungsi Ekstensi dan fungsi Lambda. Sekarang kita bisa pergi ke fungsi Ekstensi satu per satu.
membiarkan
public inline fun <T, R> T.let(block: (T) -> R): R = block(this)
Dua Tipe T dan R digunakan dalam fungsi di atas.
T.let
T
bisa berupa objek seperti kelas String. sehingga Anda dapat menjalankan fungsi ini dengan objek apa pun.
block: (T) -> R
Di parameter let, Anda bisa melihat fungsi lambda di atas. Selain itu, objek pemanggilan dilewatkan sebagai parameter fungsi. Jadi Anda bisa menggunakan objek kelas pemanggilan di dalam fungsi. lalu mengembalikan R
(objek lain).
Contoh:
val phoneNumber = "8899665544"
val numberAndCount: Pair<Int, Int> = phoneNumber.let { it.toInt() to it.count() }
Dalam contoh di atas, mari kita gunakan String sebagai parameter fungsi lambda dan mengembalikan Pair sebagai gantinya.
Dengan cara yang sama, fungsi ekstensi lainnya berfungsi.
juga
public inline fun <T> T.also(block: (T) -> Unit): T { block(this); return this }
extension function also
mengambil kelas pemanggilan sebagai parameter fungsi lambda dan tidak mengembalikan apa-apa.
Contoh:
val phoneNumber = "8899665544"
phoneNumber.also { number ->
println(number.contains("8"))
println(number.length)
}
menerapkan
public inline fun <T> T.apply(block: T.() -> Unit): T { block(); return this }
Sama seperti juga tetapi objek pemanggilan yang sama diteruskan sebagai fungsi sehingga Anda dapat menggunakan fungsi dan properti lainnya tanpa memanggilnya atau nama parameter.
Contoh:
val phoneNumber = "8899665544"
phoneNumber.apply {
println(contains("8"))
println(length)
}
Anda dapat melihat pada contoh di atas fungsi kelas String yang secara langsung dipanggil di dalam lambda funtion.
takeIf
public inline fun <T> T.takeIf(predicate: (T) -> Boolean): T? = if (predicate(this)) this else null
Contoh:
val phoneNumber = "8899665544"
val number = phoneNumber.takeIf { it.matches("[0-9]+".toRegex()) }
Dalam contoh di atas hanya number
akan memiliki string yang phoneNumber
cocok dengan regex
. Jika tidak, itu akan terjadi null
.
takeUnless
public inline fun <T> T.takeUnless(predicate: (T) -> Boolean): T? = if (!predicate(this)) this else null
Ini adalah kebalikan dari takeIf.
Contoh:
val phoneNumber = "8899665544"
val number = phoneNumber.takeUnless { it.matches("[0-9]+".toRegex()) }
number
akan memiliki string phoneNumber
hanya jika tidak cocok dengan regex
. Jika tidak, itu akan terjadi null
.
Anda dapat melihat jawaban serupa yang berguna di sini perbedaan antara kotlin juga, apply, let, use, takeIf dan takeUnless di Kotlin
phoneNumber. takeUnless{}
bukan phoneNumber. takeIf{}
.
Ada 6 fungsi pelingkupan yang berbeda:
Saya menyiapkan catatan visual seperti di bawah ini untuk menunjukkan perbedaannya:
data class Citizen(var name: String, var age: Int, var residence: String)
Keputusan tergantung pada kebutuhan Anda. Kasus penggunaan berbagai fungsi saling tumpang tindih, sehingga Anda dapat memilih fungsi berdasarkan konvensi spesifik yang digunakan dalam proyek atau tim Anda.
Meskipun fungsi cakupan adalah cara untuk membuat kode lebih ringkas, hindari menggunakannya secara berlebihan: ini dapat menurunkan keterbacaan kode Anda dan menyebabkan kesalahan. Hindari fungsi lingkup bersarang dan berhati-hatilah saat merangkainya: mudah untuk membuat bingung tentang objek konteks saat ini dan nilai this atau it.
Berikut adalah diagram lain untuk memutuskan mana yang akan digunakan dari https://medium.com/@elye.project/mastering-kotlin-standard-functions-run-with-let-also-and-apply-9cd334b0ef84
Beberapa konvensi adalah sebagai berikut:
Gunakan juga untuk tindakan tambahan yang tidak mengubah objek, seperti pembuatan log atau pencetakan informasi debug.
val numbers = mutableListOf("one", "two", "three")
numbers
.also { println("The list elements before adding new one: $it") }
.add("four")
Kasus umum untuk menerapkan adalah konfigurasi objek.
val adam = Person("Adam").apply {
age = 32
city = "London"
}
println(adam)
Jika Anda membutuhkan bayangan, gunakan run
fun test() {
var mood = "I am sad"
run {
val mood = "I am happy"
println(mood) // I am happy
}
println(mood) // I am sad
}
Jika Anda perlu mengembalikan objek penerima itu sendiri, gunakan apply atau juga
Menurut pengalaman saya, karena fungsi tersebut adalah gula sintaksis sebaris tanpa perbedaan kinerja, Anda harus selalu memilih salah satu yang memerlukan penulisan kode paling sedikit di lamda.
Untuk melakukan ini, pertama-tama tentukan apakah Anda ingin lambda mengembalikan hasilnya (pilih run
/ let
) atau objek itu sendiri (pilih apply
/ also
); kemudian dalam banyak kasus ketika lambda adalah ekspresi tunggal, pilih yang memiliki tipe fungsi blok yang sama dengan ekspresi itu, karena ketika ekspresi penerima, this
dapat dihilangkan, jika itu adalah ekspresi parameter, it
lebih pendek dari this
:
val a: Type = ...
fun Type.receiverFunction(...): ReturnType { ... }
a.run/*apply*/ { receiverFunction(...) } // shorter because "this" can be omitted
a.let/*also*/ { it.receiverFunction(...) } // longer
fun parameterFunction(parameter: Type, ...): ReturnType { ... }
a.run/*apply*/ { parameterFunction(this, ...) } // longer
a.let/*also*/ { parameterFunction(it, ...) } // shorter because "it" is shorter than "this"
Namun, ketika lambda terdiri dari campuran keduanya, terserah Anda untuk memilih salah satu yang lebih cocok dengan konteksnya atau Anda merasa lebih nyaman dengannya.
Juga, gunakan yang memiliki fungsi blok parameter saat dekonstruksi diperlukan:
val pair: Pair<TypeA, TypeB> = ...
pair.run/*apply*/ {
val (first, second) = this
...
} // longer
pair.let/*also*/ { (first, second) -> ... } // shorter
Berikut adalah perbandingan singkat di antara semua fungsi ini dari kursus Kotlin resmi JetBrains di Coursera Kotlin untuk Pengembang Java :