Kotlin é há muito tempo a linguagem de programação dominante no Android. Uma das razões pelas quais gosto dessa linguagem é que as funções nela são objetos de primeira classe . Ou seja, uma função pode ser passada como parâmetro, usada como valor de retorno e atribuída a uma variável. Além disso, em vez de uma função, você pode passar um lambda . Recentemente, tive um problema interessante relacionado à substituição do lambda por uma referência de função.
Imagine que temos uma classe Button
que recebe uma função no construtor como parâmetroonClick
class Button(
private val onClick: () -> Unit
) {
fun performClick() = onClick()
}
E há uma classe ButtonClickListener
que implementa a lógica de cliques de botão
class ButtonClickListener {
fun onClick() {
print(" ")
}
}
Na classe ScreenView
, armazenamos uma variável lateinit var listener: ButtonClickListener
e criamos um botão, ao qual é passado um lambda, dentro do qual o método é chamadoButtonClickListener.onClick
class ScreenView {
lateinit var listener: ButtonClickListener
val button = Button { listener.onClick() }
}
No método, main
criamos um objeto ScreenView
, inicializamos a variável listener
e simulamos um clique no botão
fun main() {
val screenView = ScreenView()
screenView.listener = ButtonClickListener()
screenView.button.performClick()
}
Depois de iniciar o aplicativo, tudo funciona bem e a linha "Botão pressionado" é exibida.
Agora vamos voltar para a classe ScreenView
e olhar para a linha onde o botão foi criado - val button = Button { listener.onClick() }
. Você deve ter notado que o método ButtonClickListener.onClick
é semelhante em assinatura à função onClick: () -> Unit
que o construtor de nosso botão assume, o que significa que podemos substituir a expressão lambda por uma referência de função. Como resultado, obtemos
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;
, , , , , , .
Isso leva ao seguinte problema interessante: O que será impresso após o início do 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
Responda
3
Obrigado pela leitura, espero que alguém tenha se interessado e útil!