Kotlin。ラムダvs関数リファレンス

Kotlinは長い間Androidの主流のプログラミング言語でした。この言語が好きな理由の1つは、その中の関数がファーストクラスのオブジェクトであるということですつまり、関数をパラメーターとして渡し、戻り値として使用して、変数に割り当てることができます。また、関数の代わりに、いわゆるラムダを渡すことができますそして最近、ラムダを関数参照に置き換えることに関連する興味深い問題が発生しました。





Button



コンストラクターで関数をパラメーターとして受け取るクラスがあると想像してください。onClick







class Button(
    private val onClick: () -> Unit
) {
    fun performClick() = onClick()
}
      
      



そしてButtonClickListener



、ボタンクリックのロジックを実装するクラスがあります





class ButtonClickListener {
    fun onClick() {
        print(" ")
    }
}
      
      



クラスScreenView



では、変数を格納lateinit var listener: ButtonClickListener



し、ラムダが渡されるボタンを作成します。このボタン内でメソッドが呼び出されます。ButtonClickListener.onClick







class ScreenView {
    lateinit var listener: ButtonClickListener
    val button = Button { listener.onClick() }
}
      
      



このメソッドでmain



は、オブジェクトを作成しScreenView



、変数listener



初期化して、ボタンのクリックをシミュレートします。





fun main() {
    val screenView = ScreenView()
    screenView.listener = ButtonClickListener()
    screenView.button.performClick()
}
      
      



アプリケーションを起動すると、すべて正常に動作し、「ボタンが押されました」という行が表示されます。





それでは、クラスに戻って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;
      
      



, , , , , , .





これにより、次の興味深い問題が発生します。プログラムの開始後に何が出力されますか?





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







回答

3





読んでくれてありがとう、誰かが興味を持って助けてくれたことを願っています!








All Articles