7
\$\begingroup\$

In several places, I have some code that looks like the following, the only difference is in the type of listener.

public class CustomFragment extends android.app.Fragment {
    SomeListener listener;

    @Override
    public void onAttach(android.app.Activity activity) {
        super.onAttach(activity);

        // duplication
        //     ||
        //    \||/
        //     \/
        try {
            listener = (SomeListener) activity;
        } catch (ClassCastException e) {
            throw new ClassCastException(activity.toString()
                    + " must implement SomeListener");
        }
    }
}

To avoid duplication, I wrote this simple class:

public class TypeConvertor {
    private final Object whatNeedToCast;

    public static TypeConvertor cast(Object whatNeedToCast) {
        return new TypeConvertor(whatNeedToCast);
    }

    private TypeConvertor(Object whatNeedToCast) {
        this.whatNeedToCast = whatNeedToCast;
    }

    public <OtherType> OtherType to(Class<OtherType> toWhichToCast) {
        try {
            return (OtherType) whatNeedToCast;
        } catch (ClassCastException e) {
            throw new ClassCastException(
                    whatNeedToCast.toString() + " must implement " + toWhichToCast.getName()
            );
        }
    }
}

And the previous code is simplified to the following:

public class CustomFragment extends android.app.Fragment {
    SomeListener listener;

    @Override
    public void onAttach(android.app.Activity activity) {
        super.onAttach(activity);
        listener = TypeConvertor.cast(activity).to(SomeListener.class);
    }
}

But I do not like name TypeConvertor. Help me pick a better name (and help refactor this simple class if needed).

\$\endgroup\$
  • \$\begingroup\$ I don't understand yourproblem but, instead of having a type converter, should CustomerFragment be a Generic class, whose type variable is the type of SomeListener (so that a specific CustomerFragment type supports a specific type of SomeListener/Activity)? \$\endgroup\$ – ChrisW Jan 12 '14 at 14:15
  • \$\begingroup\$ Activity can implement several interfaces. \$\endgroup\$ – leonideveloper Jan 12 '14 at 14:20
6
\$\begingroup\$

It seems to me like what you have done here is to create an entire class to avoid duplicating one line. (Or OK, 5 lines if counting the try-catch statement)

I don't agree with the need to create a class for this. Especially not a generic one, since this line of code:

return (OtherType) whatNeedToCast;

Does the same thing as:

return OtherType.class.cast(whatNeedToCast);

So all you are doing additionally is to wrap it in another exception which is explained just slightly better. But personally, I think this message...

com.yourpackage.YourActivity@1df59a42 must implement com.yourpackage.SomeListener

...actually says just as much as the default one, if you would not catch the exception in the first place:

java.lang.ClassCastException: Cannot cast com.yourpackage.YourActivity to com.yourpackage.SomeListener

Actually, Cannot cast (class) to (class) would say even more if you would override the toString method in YourActivity.

If you are writing these classes to be used as a library by many other people, then it could be good practice to explain that the activity must implement some interface. Otherwise, I don't see the point of it. But either way, there's no need to create an entire class for this.

Summary:

Get rid of your additional class, just cast your activity to SomeListener and don't catch the exception to throw your own.

So in the end, all that remains is this one line:

listener = (SomeListener) activity;

And it's perfectly OK to duplicate that.

| improve this answer | |
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.