Variabel nullable dan smart cast

Aug 30 2020

Pertimbangkan blok Kotlin berikut.

var nullableInt: Int? = null

if (nullableInt != null) {
    val checkedInt: Int = nullableInt
    print("not-null-branch")
} else {
    print("null-branch")
}

Android Studio memberi tahu saya bahwa smart cast dari Int?ke Inttidak memungkinkan, karena nullableIntbisa berubah. Saya memahami bahwa ini mungkin menjadi masalah pada kode multithread.

Salah satu cara untuk menangani masalah ini adalah dengan membuat cast eksplisit val checkedInt: Int = nullableInt!!, tetapi jika saya akan menggunakan kode di lingkungan multithread, itu tidak disarankan.


Tutup duplikat

Ada beberapa pertanyaan yang sangat dekat tentang SO mengenai topik ini. Namun, saya tidak menemukan jawaban satistafctory di salah satu jawaban yang saya temukan:

Di Kotlin, apa cara idiomatik untuk menangani nilai nullable, mereferensikan atau mengonversinya membahas mengapa masalah muncul, tetapi tidak memberikan saran tentang cara menanganinya

Kotlin menghindari smart cast untuk pemeriksaan null memiliki cabang if-not-null yang mengembalikan nilai bukan null, sehingga ?.let{} :? {}konstruksi berfungsi di sana. Karena cabang not-null saya mengembalikan null, kedua cabang akan berjalan.

Kotlin "Smart cast tidak mungkin, karena propertinya bisa saja diubah saat ini" hanya berkaitan dengan cabang-bukan-null dan tidak-cabang-null, sehingga ?.let{}konstruksinya tampak benar. Di utas itu, mereka memberikan saran tentang mengambil salinan lokal sebelum ifpernyataan, yang dapat dilakukan dalam kasus saya juga. Sayangnya tidak terlalu elegan, dan saya harap ada alternatif lain.


Apakah ada cara untuk menangani percabangan bersyarat null ini dengan cara yang aman nol tanpa mengambil salinannya?

Saya memahami bahwa jawabannya berpotensi menjadi "tergantung". Jika demikian, harap katakan dan jelaskan alasannya.

Jawaban

3 Sam Aug 30 2020 at 11:35

Gunakan .letsebagai ganti?.let

Karena .letfungsi ekstensi ditentukan untuk semua jenis, termasuk yang nullable, Anda sebenarnya dapat memanggilnya tanpa ?.operator panggilan aman . Saat Anda melakukan itu, lambda akan selalu dipanggil, bahkan untuk nullnilai. Parameter di dalam letblok akan menjadi null jika penerima adalah nullable.

Namun, parameter lambda adalah memenuhi syarat untuk pengecoran pintar , karena tidak bisa berubah.

Inilah perbedaannya:

x.let { it -> /* This always runs. 'it' can be null if 'x' is null */ }
x?.let { it -> /* This only runs if 'x' is not null. 'it' is never null. */ }

Menerapkannya pada contoh Anda, Anda dapat menulis ini:

var nullableInt: Int? = null

nullableInt.let {
    if (it != null) {
         doSomethingWith(it)
    } else {
         doSomethingElse()
    }
}
2 Tenfour04 Aug 30 2020 at 12:57

Anda tidak dapat mulai mentransmisikan properti karena thread lain mungkin sedang memodifikasinya. Tidak ada cara logis untuk menyiasatinya. Bahasa tersebut telah menyediakan cara yang tidak aman untuk melakukannya dengan !!yang telah Anda sebutkan.

Membuat salinan referensi lokal (baik secara manual atau dengan menggunakan fungsi lingkup seperti withatau let) adalah hal yang sepele jadi itu bukan sesuatu yang perlu Anda khawatirkan.

Pendapat pribadi saya adalah variabel lokal adalah cara terbersih dan paling mudah dibaca untuk melakukannya. Anda menghindari penumpukan blok yang Anda miliki dengan fungsi cakupan.

val myVar = myProp
if (myVar != null) {

} else {

}

Saya pikir cara terbersih dengan fungsi ruang lingkup adalah dengan menggunakan with. Pembacaannya lebih baik daripada letsaat Anda menangani kedua cabang.

with(myProp) {
    if (this != null) {

    } else {

    }
}

Untuk cara singkat menangani kedua cabang, berikut ini cara kerjanya. Saya menganggap alsomenjadi sedikit lebih kuat daripada letuntuk situasi ini, karena Anda tidak dapat secara tidak sengaja menjalankan kedua cabang dengan mengembalikan null dari lambda pertama. Tapi keterbacaan menderita karena singkat ini.

myProp?.also {
    // not null it
} ?: run {
    // null
}