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()
}
FirstFirst
FirstSecond
SecondFirst
SecondSecond
回答
3
読んでくれてありがとう、誰かが興味を持って助けてくれたことを願っています!