2

We all agree that the use of instanceOf is usually not the best solution. There are plenty of examples in the web.

But lets consider for a second the following example, where we need to call a method from a fragment to its activity:

public class BaseActvity extends FragmentActivity implements ISomething {
    @Override
    public void doSomething();
}

Then lets say we have a fragment in the app that needs to call doSomething() :

public MyFragment extends Fragment() {

public void onCreate() {
    public void onResume() {
        Activity activity = getActivity();
        if (activity != null && activity instanceOf ISomething) {
            ISomething something = (ISomething) activity;
            something.doSomething();
        }
    }
}

public interface ISomething {
    void doSomething();
}

As you can see, we cannot guarantee that getActivity() will return an object of ISomething, so we check the type. An improvement would be to add an interface, but we would still to check the getActvity() return type to protect our code.

Because of the nature of the Android Framework and the getActivity() call, I can't find a better solution. Maybe someone can help with some input..

NOTE: Ive added an interface to follow the visitor pattern. Notice that I still have to use instanceOf to assure that the parent activity is implementing it.

Thank you .

Gaspar de Elais

Gaspar de Elias
  • 244
  • 3
  • 12
  • 1
    You could use EventBus: https://github.com/greenrobot/EventBus – MatF Dec 17 '15 at 15:14
  • 'We all agree that the use of instanceOf is usually not the best solution. '. ? No. I don't. – greenapps Dec 17 '15 at 15:17
  • @greenapps Back in the old days, using instanceOf had two drawbacks: 1. Not efficient, 2. If we forget to implement certain interface in some class, you could endup in crash or hiding the problem. Problem #1 was solved by modern JVM/JIC optimizations (http://stackoverflow.com/questions/103564/the-performance-impact-of-using-instanceof-in-java), but #2 is still an issue. When possible, a good design can detect #2 in compile time, like using the Visitor pattern: http://stackoverflow.com/questions/8841577/is-this-use-of-the-instanceof-operator-considered-bad-design – Gaspar de Elias Dec 17 '15 at 16:56

2 Answers2

6

You are correct, it would be better to define an interface in your Fragment and require the Activity to implement it. Then, in your onAttach() you cast your Activity to the interface in a try/catch and if it throws a ClassCastException you can throw your own exception.

public MyFragment extends Fragment {
    private Callback  mCallback;

    public interface Callback {
        void doSomething();
    }
    ...

    @Override
    public void onAttach(Activity activity) {
        try {
            mCallback = (Callback)activity;
        } catch (ClassCastException e) {
            throw new IllegalArgumentException("Activity must implement Callback");
        }
    }
}

In fact, this is what is recommended/described in this Fragment page on the main Android developer site: http://developer.android.com/training/basics/fragments/communicating.html

Larry Schiefer
  • 15,687
  • 2
  • 27
  • 33
  • Thanks, ive already added an interface, but still using instanceOf. What is the advantage of catching an exception vs instanceOf from your point of view? Efficiency? Code legibility? throwing an RuntimeException seems to hash IMHO. – Gaspar de Elias Dec 17 '15 at 15:16
  • By using an exception, you are enforcing what you intended: any `Activity` which uses this `Fragment` must support the interface. You could do the same with `instanceOf` and just throw an exception if it fails. In terms of performance, both use forms of reflection but the exception is more expensive since it will have to create the new object. In terms of readability, the exception is much more clear IMHO that this is the expected usage pattern. – Larry Schiefer Dec 17 '15 at 16:07
  • You are right, you are throwing the exception in onAttach which makes sense, thank you. I will mark the question as answered. Cheers. – Gaspar de Elias Dec 17 '15 at 16:16
0

You could take a look at the Visitor Pattern. Here is an explanation and some Java code.

Jelle van Es
  • 613
  • 6
  • 20
  • Unfortunately for this particular case, where the Activity class is a System class, and the reference is got from calling `getActivity()` it is not possible to avoid instanceOf / or / ClassCastException catch block. As you can see, I've updated the code in order to use the Visitor pattern using interface ISomething, but we still have the cast issue. – Gaspar de Elias Dec 17 '15 at 17:00