About smart cast in callback from Fragment to Activity in Kotlin

Introduction

While migrating an existing Android application from Java to Kotlin, I was addicted to implementing Listener (interface) in Fragment and writing code that calls an event at the timing of UI operation, so a memo at that time.

environment

What I was trying to do

Define a Listener to be called from Activity on the Fragment side, cast the context to Listener if the argument context implements interface when the onAttach method is called, and call the Activity event method when the button is pressed The following Java code I tried to rewrite it in Kotlin.

MyFragment.java


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;
    		}
	}

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

    //...
}

MyFragment.kt



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

    //...
}

Then, when you call mListener.onClickButton ()

Smart cast to 'MyFragment.FragmentListener’ is impossible, because 'mListener’ is a mutable property that could have been changed by this time

I get an error.

This is an error that smart cast to FragmentListener cannot be performed because rewriting from another thread may occur because mListener is declared with var.

However, if you declare it with val, context cannot be assigned.

solution

Therefore, this time, I solved it by using the scope function let without doing smart cast.

Change before


if (mListener != null){
	mListener.onClickButton()
}

After change


mListener?.let { it.onClickButton() }

If mListener is null, the ?. call does not executelet {}, and if it is not null, the processing inside let {} is executed.

In this case, the following description can also be made.


mListener?.let(FragmentListener::onClickButton)

Reference material

Kotlin Reference: Type Checks and Casts: 'is' and 'as'

Summary of usage of Kotlin scope function

[Android] Introduction to Kotlin for App Engineers (Basic Grammar)

Recommended Posts

About smart cast in callback from Fragment to Activity in Kotlin
What I thought about when I started migrating from Java to Kotlin
Try to get data from database using MyBatis in Micronaut + Kotlin project
A story about converting character codes from UTF-8 to Shift-jis in Ruby
[Kotlin] 3 ways to get Class from KClass
Memo for migration from java to kotlin
Summary of points I was worried about when migrating from java to kotlin