Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Classes with a future compatibility that won't break future modifications

I was reading the source code for Android's RecyclerView and I was using the SimpleOnItemTouchListener and reading the documentation about this class. But I'm not sure that I understand the meaning of this:

Another benefit of using this class is future compatibility. As the interface may change, we'll always provide a default implementation on this class so that your code won't break when you update to a new version of the support library

Is this because the SimpleOnItemTouchListener implements the OnItemTouchListener and provides some default behavior? So if the OnItemTouchListener gets updated the SimpleOnItemTouchListener would still return the default behavior.

The part about "if the interface may change". Are they talking about the OnItemTouchListener?

However, the SimpleOnItemTouchListener just seems to have empty methods and nothing else.

like image 757
ant2009 Avatar asked Mar 09 '19 22:03

ant2009


4 Answers

Let's say you have this interface:

public interface OnItemTouchListener {
    boolean onInterceptTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e);
    void onTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e);
    void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept);
}

And you decide to implement it on your own:

public class MyOwnOnItemTouchListener implements OnItemTouchListener {

    @Override
    boolean onInterceptTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e) {
        boolean result = doSomething(e);
        return result;
    }

    @Override
    void onTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e) {
        doSomethingElse(rv, e);
        doSomethingMore(rv);
    }

    @Override
    void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
        if (disallowIntercept) {
            doADifferentThing();
        }
    }
}

Everything is fine...
... until, six months from now, OnItemTouchListener is modified to introduce a new method:

public interface OnItemTouchListener {
    boolean onInterceptTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e);
    void onTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e);
    void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept);
    // New method
    void onMultiTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e);
}

And all of a sudden your app won't compile anymore 😱

Error: MyOwnOnItemTouchListener is not abstract and does not override abstract method onMultiTouchEvent() in OnItemTouchListener

And it's not even your fault, you didn't change anything! It's just that the interface changed and your code is not up to date with that change.


To avoid this, the API developers offer you a "default" implementation class, SimpleOnItemTouchListener, that is guaranteed to always be up to date with the interface, and that you can extend instead:

public class SimpleOnItemTouchListener implements OnItemTouchListener {
    // empty, override in your class 
    boolean onInterceptTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e) { return false; }
    // empty, override in your class 
    void onTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e) {}
    // empty, override in your class 
    void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {}
}

So instead of directly implementing the interface, you can do this:

public class MyOwnOnItemTouchListener extends SimpleOnItemTouchListener { //extend Simple instead of implementing interface

    @Override
    boolean onInterceptTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e) {
        boolean result = doSomething(e);
        return result;
    }

    @Override
    void onTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e) {
        doSomethingElse(rv, e);
        doSomethingMore(rv);
    }

    @Override
    void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
        if (disallowIntercept) {
            doADifferentThing();
        }
    }
}

Now, if in six months the API devs need to introduce a new method, they will change both classes, as guaranteed:

public interface OnItemTouchListener {
    boolean onInterceptTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e);
    void onTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e);
    void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept);
    // New method
    void onMultiTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e);
}
public class SimpleOnItemTouchListener implements OnItemTouchListener {
    // empty, override in your class 
    boolean onInterceptTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e) { return false; }
    // empty, override in your class 
    void onTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e) {}
    // empty, override in your class 
    void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {}
    // New method
    // empty, override in your class 
    void onMultiTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e) {}
}

And now, despite this changes, MyOwnOnItemTouchListener will still compile, even though it doesn't implement onMultiTouchEvent, because if at any point MyOwnOnItemTouchListener.onMultiTouchEvent() is called it will just use the (empty) implementation from its parent, SimpleOnItemTouchListener.

And your app will keep working 💪


Now, answering your exact questions:

Is this because the SimpleOnItemTouchListener implements the OnItemTouchListenerand provides some default behavior?

Yes. Though here, the "default behavior" is "do nothing at all", so you still need to implement the methods in your own listener if you want to actually do something.

So if the OnItemTouchListener gets updated the SimpleOnItemTouchListener would still return the default behavior.

Yes, exactly.

The part about "if the interface may change". Are they talking about the OnItemTouchListener?

Yes.

However, the SimpleOnItemTouchListener just seems to have empty methods and nothing else.

Yes. The "default behavior" they provide is just "do nothing". It's just a way of avoiding compilation failures.
You still have to implement the methods in a meaningful way. But now you have a safety net if new methods are ever introduced.

I'd say you actually understood the gist of it, it's just that the empty default implementation was confusing.

like image 192
walen Avatar answered Nov 14 '22 18:11

walen


Is this because the SimpleOnItemTouchListener implements the OnItemTouchListener and provides some default behavior?

no, it simply implements interface with empty methods. As you know when you implement interface, then you need to add definitions for all the interface methods, otherwise you will get compile error. But if you extend a class then you dont need to override all the base methods (well... unless they are not abstract - but that is not a case here).

The part about if the interface may change. Are they talking about the OnItemOnTouchListener?

yes, they talk about the interface RecyclerView.OnItemTouchListener being changed:

http://androidxref.com/9.0.0_r3/xref/frameworks/support/v7/recyclerview/src/main/java/androidx/recyclerview/widget/RecyclerView.java#OnItemTouchListener

suppose they add to RecyclerView.OnItemTouchListener a new method : void foo(), then if you upgrade support library and you had directly implemented in your class RecyclerView.OnItemTouchListener, then you will get compiler error (you will need to implement foo() in your class). In the comment you quote android team is making a promiss that they will implement foo() in SimpleOnItemTouchListener so if you extend it in your MyOnItemTouchListener they will already have empty implementation - so no compile error.

like image 31
marcinj Avatar answered Nov 14 '22 17:11

marcinj


I find it more or less a Adapter implementation. As android is continuously evolving , the interactions are never going to be the same and will keep evolving with the OS.

Here, the root interface OnItemTouchListener can be implemented, so that the application gets control on the touch events that was consumed or being consumed. To put in simple words,OnItemTouchListener says "Do you want to handle the touch events? Implement me! But be ready to handle all kinds of new gestures I catch for you.I'm dynamic"

Now another guy SimpleOnItemTouchListener comes in between and says, "Hey, I can be your consultant with the OnItemTouchListener .We can have an agreement on what is to be handled . Even if OnItemTouchListener goes mad with new stuff, I will help you to remain calm and not change. I will take the pain and make sure you are not disturbed "

So its simple, OnItemTouchListener may evolve in time with Android. SimpleOnItemTouchListener might evolve with OnItemTouchListener, but will not deprecate or become vestigial in any of the current behaviours.

Adding more, since SimpleOnItemTouchListener gives you a default implementation, your code will look neat because you need to override only what you need.

like image 5
Kris Avatar answered Nov 14 '22 17:11

Kris


I'm currently working on a plugin framework for the project I'm working on. I've attempted to solve this problem through versioning my interface. I do have a base implementation like walen talks about. However, I worry about the people who implemented the interface instead of the base implementation. Surely if I change my interface and release I've just broken those plugins that decided not to extend my base class.

The core application uses whatever version of the interface is required for that part of the code. A V2 version of the interface would extend the V1, a V3 extending V2, etc. Instead of calling all of the plugins directly I have a manager class that does that work. That allows me to ensure that if a V3 feature is attempting to execute I'm only doing so on plugins that implemented the V3 version of the plugin interface.

If I was only exposing the base class and hide the interface from the public API I could probably get by with just a base implementation that others extend. Unfortunately since the interface is also public I have to make sure I don't break developers that didn't extend the base class.

like image 5
Logan K Avatar answered Nov 14 '22 19:11

Logan K