Kotlin telah lama menjadi bahasa pemrograman utama di Android. Salah satu alasan mengapa saya menyukai bahasa ini adalah karena fungsi di dalamnya adalah objek kelas satu . Artinya, suatu fungsi dapat dilewatkan sebagai parameter, digunakan sebagai nilai kembali, dan ditetapkan ke variabel. Selain itu, alih-alih fungsi, Anda bisa meneruskan apa yang disebut lambda . Dan baru-baru ini saya punya masalah menarik terkait mengganti lambda dengan referensi fungsi.
Bayangkan kita memiliki kelas Button
yang menerima fungsi dalam konstruktor sebagai parameteronClick
class Button(
private val onClick: () -> Unit
) {
fun performClick() = onClick()
}
Dan ada kelas ButtonClickListener
yang mengimplementasikan logika klik tombol
class ButtonClickListener {
fun onClick() {
print(" ")
}
}
Di kelas ScreenView
, kami menyimpan variabel lateinit var listener: ButtonClickListener
dan membuat tombol, yang diteruskan lambda, di dalamnya metode ini dipanggilButtonClickListener.onClick
class ScreenView {
lateinit var listener: ButtonClickListener
val button = Button { listener.onClick() }
}
Dalam metode ini, main
kami membuat objek ScreenView
, menginisialisasi variabel listener
dan mensimulasikan mengklik tombol
fun main() {
val screenView = ScreenView()
screenView.listener = ButtonClickListener()
screenView.button.performClick()
}
Setelah memulai aplikasi, semuanya bekerja dengan baik dan baris "Tombol ditekan" ditampilkan.
Sekarang mari kembali ke kelas ScreenView
dan lihat baris tempat tombol dibuat - val button = Button { listener.onClick() }
. Anda mungkin telah memperhatikan bahwa metode ButtonClickListener.onClick
ini memiliki tanda tangan yang mirip dengan fungsi onClick: () -> Unit
yang diambil oleh konstruktor tombol kita, yang berarti bahwa kita dapat mengganti ekspresi lambda dengan referensi fungsi. Hasilnya, kita dapatkan
class ScreenView {
lateinit var listener: ButtonClickListener
val button = Button(listener::onClick)
}
- listener
Exception in thread "main" kotlin.UninitializedPropertyAccessException: lateinit property listener has not been initialized
at lambdas.ScreenView.<init>(ScreenView.kt:6)
at lambdas.ScreenViewKt.main(ScreenView.kt:10)
at lambdas.ScreenViewKt.main(ScreenView.kt)
, Java . .
Function0
invoke
, . - listener.onClick()
private final Button button = new Button((Function0)(new Function0() {
public final void invoke() {
ScreenView.this.getListener().onClick();
}
}));
, listener
.
. Function0
, invoke()
, , onClick
this.receiver
. receiver
Function0
listener
, listener
lateinit
, receiver
- listener
null
, . .
Button var10001 = new Button;
Function0 var10003 = new Function0() {
public final void invoke() {
((ButtonClickListener)this.receiver).onClick();
}
};
ButtonClickListener var10005 = this.listener;
if (var10005 == null) {
Intrinsics.throwUninitializedPropertyAccessException("listener");
}
var10003.<init>(var10005);
var10001.<init>((Function0)var10003);
this.button = var10001;
, , , , , , .
Ini mengarah pada masalah menarik berikut: Apa yang akan dicetak setelah program dimulai?
class Button(
private val onClick: () -> Unit
) {
fun performClick() = onClick()
}
class ButtonClickListener(
private val name: String
) {
fun onClick() {
print(name)
}
}
class ScreenView {
var listener = ButtonClickListener("First")
val buttonLambda = Button { listener.onClick() }
val buttonReference = Button(listener::onClick)
}
fun main() {
val screenView = ScreenView()
screenView.listener = ButtonClickListener("Second")
screenView.buttonLambda.performClick()
screenView.buttonReference.performClick()
}
FirstFirst
FirstSecond
SecondFirst
SecondSecond
Menjawab
3
Terima kasih telah membaca, semoga ada yang tertarik dan bermanfaat!