Kotlin ha sido durante mucho tiempo el lenguaje de programaci贸n principal en Android. Una de las razones por las que me gusta este lenguaje es que sus funciones son objetos de primera clase . Es decir, una funci贸n puede pasarse como par谩metro, usarse como valor de retorno y asignarse a una variable. Adem谩s, en lugar de una funci贸n, puede pasar un llamado lambda . Y recientemente tuve un problema interesante relacionado con reemplazar la lambda con una referencia de funci贸n.
Imagina que tenemos una clase Button
que recibe una funci贸n en el constructor como par谩metroonClick
class Button(
private val onClick: () -> Unit
) {
fun performClick() = onClick()
}
Y hay una clase ButtonClickListener
que implementa la l贸gica de los clics de los botones.
class ButtonClickListener {
fun onClick() {
print(" ")
}
}
En la clase ScreenView
, almacenamos una variable lateinit var listener: ButtonClickListener
y creamos un bot贸n, al que se le pasa una lambda, dentro del cual se llama al m茅todoButtonClickListener.onClick
class ScreenView {
lateinit var listener: ButtonClickListener
val button = Button { listener.onClick() }
}
En el m茅todo, main
creamos un objeto ScreenView
, inicializamos la variable listener
y simulamos hacer clic en el bot贸n
fun main() {
val screenView = ScreenView()
screenView.listener = ButtonClickListener()
screenView.button.performClick()
}
Despu茅s de iniciar la aplicaci贸n, todo funciona bien y se muestra la l铆nea "Bot贸n presionado".
ScreenView
, - val button = Button { listener.onClick() }
. , ButtonClickListener.onClick
onClick: () -> Unit
, , , .
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;
Es decir, la diferencia entre una lambda y una referencia de funci贸n es que cuando se pasa una referencia de funci贸n, la variable a cuyo m茅todo nos referimos se fija en la creaci贸n y no en la ejecuci贸n, como sucede cuando se pasa una lambda.
Esto conduce al siguiente problema interesante: 驴Qu茅 se imprimir谩 despu茅s de que se inicie el programa?
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
Responder
3
Gracias por leer, espero que alguien est茅 interesado y sea 煤til.