Contoh kapan kita harus menggunakan run, let, apply, juga dan dengan di Kotlin


108

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:


129

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 thisdan oleh karena itu kita dapat mengaturnya seed, hashdan hashRepetitionstanpa menggunakan variabel. generate()akan mengembalikan contoh Password.

applyserupa, 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 thisakan 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 itsekarang 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 applysini akan membuat bayangan this, jadi itu this.weightakan mengacu pada apel, dan bukan pada keranjang buah.


Catatan: Saya tanpa malu-malu mengambil contoh dari blog saya


6
Bagi siapa pun seperti saya yang dikejutkan oleh kode pertama, baris terakhir lambda dianggap sebagai nilai kembali di Kotlin.
Jay Lee

66

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.

masukkan deskripsi gambar di sini

Silakan juga lihat grafik yang menyertai penjelasan saya di bawah ini.

Konsep

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.

Contoh Konsep

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 thisdi 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)
}

Cara Memandangnya

Saya cenderung melihat alsodan letsebagai sesuatu yang eksternal, di luar. Setiap kali Anda mengucapkan dua kata ini, itu seperti Anda mencoba untuk bertindak atas sesuatu. letinstal virus di komputer ini, dan alsohancurkan. Jadi, ini menunjukkan apakah Anda seorang aktor atau bukan.

Untuk bagian hasil, jelas ada. alsomenyatakan 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 :)


1
Diagram menjelaskan semuanya; yang terbaik sejauh ini.
Shukant Pal

8

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 Stringkelas.

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

Tbisa 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 alsomengambil 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 numberakan memiliki string yang phoneNumbercocok 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()) }

numberakan memiliki string phoneNumberhanya 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


Anda salah ketik dalam contoh terakhir Anda, yang mungkin Anda maksudkan, phoneNumber. takeUnless{}bukan phoneNumber. takeIf{}.
Ryan Amaral

1
Diperbaiki. Terima kasih @Ryan Amaral
Bhuvanesh BS

8

Ada 6 fungsi pelingkupan yang berbeda:

  1. T. lari
  2. T.let
  3. T.apply
  4. T. juga
  5. dengan
  6. Lari

Saya menyiapkan catatan visual seperti di bawah ini untuk menunjukkan perbedaannya:

data class Citizen(var name: String, var age: Int, var residence: String)

masukkan deskripsi gambar di sini

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 masukkan deskripsi gambar di sini

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


4

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, thisdapat dihilangkan, jika itu adalah ekspresi parameter, itlebih 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 : Tabel perbedaan Penerapan yang disederhanakan

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.