はじめに
既存のAndroidアプリをJavaからKotlinへの移行中に、FragmentにListener(interface)を実装し、UI操作がされたタイミングでイベントを呼ぶコードを書く際にハマったのでその時のメモ。
環境
- AndroidStudio: 3.0.1
- Kotlin: 1.2.10
しようとしていたこと
Fragment側でActivityから呼ぶ Listenerを定義し、onAttachメソッドが呼ばれた時に引数のcontextがinterfaceを実装していたらcontextをListenerにキャストし、ボタンを押されたらActivityのイベントメソッドを呼ぶ以下のJavaコードをKotlinで書き直そうとした。
public class MyFragment extends Fragment {
public interface FragmentListener {
void onClickButton();
}
private FragmentListener mListener;
@Override
public void onAttach(Context context){
//...
if (context instanceof FragmentListener){
mListener = (FragmentListener) context;
}
// 上記を簡易に書くなら
// mListener = context as? FragmentListener
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState){
//...
view.findViewById(R.id.Button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v){
if (mListener != null){
mListener.onClickButton();
}
}
});
}
//...
}
class MyFragment : Fragment() {
interface FragmentListener {
fun onClickButton()
}
private var mListener: FragmentListener? = null
override fun onAttach(context: Context){
//...
if (context is FragmentListener){
mListener = context
}
}
override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
//...
view!!.findViewById<Button>(R.id.Button).setOnClickListener {
if (mListener != null){
mListener.onClickButton()
}
}
}
//...
}
とすると、mListener.onClickButton()を呼んだところで
Smart cast to 'MyFragment.FragmentListener’ is impossible, because 'mListener’ is a mutable property that could have been changed by this time
とエラーが出る。
これは、mListener
をvar
で宣言したために別スレッドからの書き換えが発生する可能性があるためFragmentListenerへのスマートキャストができないという内容のエラー。
しかしval
で宣言してしまうとcontextが代入できない。
解決策
そのため、今回はスマートキャストをせず、スコープ関数let
を使うことで解決した。
if (mListener != null){
mListener.onClickButton()
}
mListener?.let { it.onClickButton() }
mListener
がnull
なら?.
呼び出しでlet{}
を実行せず、null
でないならlet{}
内の処理が実行される。
また、この場合以下のような記述もできる
mListener?.let(FragmentListener::onClickButton)
// より簡易に書くなら
mListener?.onClickButton()
参考資料
Kotlin Reference: Type Checks and Casts: 'is' and 'as'