2

I have a super class (Android's Fragment):

public abstract class BaseFragment<T> extends Fragment {

    private T mListener;

    public T getFragmentInteractionListener(){
        return mListener;
    }

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        castFragmentInteractionListener();
    }

    void castFragmentInteractionListener(){         
        Type superClassType = this.getClass().getGenericSuperclass();
        if (superClassType instanceof ParameterizedType) {
            try {
                // I want to know if there is any way to check if the type argument has 
                // been set? So I can avoid the exception.
                Class<T> listenerClazz = (Class<T>) ((ParameterizedType) superClassType).getActualTypeArguments()[0];

                mListener = FragmentUtils.castFragmentInteractionListener(context, this, listenerClazz);

            } catch (Exception e) {

            }
        }

    }
}

and some sub classes derived from it.

public class FragmentDoesNotNeedListener extends BaseFragment {


}

public class FragmentHasListener extends BaseFragment<FragmentInteractionListener> {

    interface FragmentInteractionListener {
        void onClick();
        void onExit();
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        getFragmentInteractionListener().onClick();

    }

}

Essentially, I want each fragment to derive from the BaseFragment, most of them have the listener that can be called from themselves. I don't want to repeat the code to cast it, therefore create the base fragment and use generic type. However, some of the fragments do not need to use listener. But I don't how to check if the argument type is given or not.

In BaseFragment class, the method castFragmentInteractionListener casts the class to a class field, and then uses it to cast the class into the listener. It is OK if the child class (FragmentDoesNotNeedListener) does not pass the generic type argument because the exception will be caught. However, I wonder if there is any way to check instead of catching the exception? Or maybe this is not a good design...

Arst
  • 3,098
  • 1
  • 35
  • 42

1 Answers1

0

According to this StackOverflow post

Generic informations are erased at runtime, it cannot be recovered. A workaround is to pass the class T in parameter of a static method :

public class MyGenericClass {

private final Class<T> clazz;

public static <U> MyGenericClass<U> createMyGeneric(Class<U> clazz) {
    return new MyGenericClass<U>(clazz);
}

protected MyGenericClass(Class<T> clazz) {
    this.clazz = clazz;
}

public void doSomething() {
    T instance = clazz.newInstance();
} } 

It's ugly, but it works.

Or you could simply perform checks on which class you are in fact using

public abstract class BaseFragment<T> extends Fragment {

//...

public static Class classType(Class cls)
        {
            if(cls.equals(FragmentDoesNotNeedListener.class))
                return FragmentDoesNotNeedListener.class;
            else
                if (cls.equals(FragmentHasListener.class))
                    return FragmentHasListener.class;
            //else
              //  if(....)

            else
                return null;
        }
}
Community
  • 1
  • 1
HenriqueMS
  • 3,864
  • 2
  • 30
  • 39
  • This not a good solution in my case. Because I have 30~40 sub fragments....However, I used to create a annotation that used Class as a parameter. ex. @ListenerClass(clazz=Fragment1Listener.class). But this not a solution I like because I don't want to put Fragment1Listener twice: one as the annotation argument and one as the generic type argument. – Arst Oct 11 '16 at 09:31