Kotlin. Lambda vs referência de função

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



  1. FirstFirst







  2. FirstSecond







  3. SecondFirst







  4. SecondSecond







Responda

3





Obrigado pela leitura, espero que alguém tenha se interessado e útil!








All Articles