0

I have a class that has a listener interface. I want to create a subclass with the listener interface being a subclass type. For example:

interface OnSadListener {
    void imSad();
}

interface OnCryingListener extends OnSadListener {
    void imCrying();
}

class Person {
    OnSadListener listener;

    void setListener(OnSadListener listener){
        this.listener = listener;
    }
}

class Baby extends Person {
    OnCryingListener listener; // I want it to override the original one in Person

    void cry(){
        listener.imSad();
        listener.imCrying();
    }
}

void doIt(){
    Baby michael = new Baby();

    michael.setListener(new OnCryingListener() {
        @Override void imSad(){
            System.out.print("I'm sad");
        }

        @Override void imCrying(){
            System.out.print("Wahhhhhh!");
        }
    });

    michael.cry(); // the imCrying funtion is empty (setListener setted Person's listener and not Baby's)
}

I know Baby.setListener(new OnCryingListener(){...}); would work because OnCryingListener is a subclass of OnSadListener. But then I would not be able to call imCrying() or it would be empty.

Is there a way for OnCryingListener listener to "override" Person's OnSadListener listener?

Iaka Noe
  • 43
  • 8

2 Answers2

0

You need to override the setListener(OnSadListener) method in the Baby class, because you are never setting the listener.

class Baby extends Person {
    OnCryingListener listener;

    @Override
    void setListener(OnSadListener listener) {
        this.listener = (OnCryingListener) listener;
    }

    void cry() {
        listener.imSad();
        listener.imCrying();
    }
}

A more complete example is the following...

class Person {
    protected OnSadListener listener; // Make it protected or add accessor/mutators

    void setListener(OnSadListener listener) {
        this.listener = listener;
    }
}

By making the listener protected, you can reuse the Person class' OnSadListener. You just need to cast the listener to a OnCryingListener to call the imCrying() method.

class Baby extends Person {
    @Override
    void setListener(OnSadListener listener) {
        this.listener = (OnCryingListener) listener;
    }

    void cry() {
        listener.imSad();
        ((OnCryingListener) listener).imCrying(); // Cast here
    }
}

Update

If you want to use generics, you can remove the need for casting.

public class Person <T extends OnSadListener> {
    protected T listener;

    public void setListener(T listener) {
        this.listener = listener;
    }
}

Now you can tell Baby what type of listener is is dealing with.

public class Baby extends Person<OnCryingListener> {
    @Override
    public void setListener(OnCryingListener listener) {
        this.listener = (OnCryingListener) listener;
    }

    public void cry() {
        listener.imSad();
        listener.imCrying(); // No need to cast
    }
}
Mr. Polywhirl
  • 42,981
  • 12
  • 84
  • 132
  • I'm marking this as correct. Anyway, will `((OnCryingListener) listener).imCrying();` work? I thought of doing that but I assumed once I saved the `OnCryingListener` into the `OnSadListener` field I wouldn't be able to access `imCrying()` again, it being "deleted". – Iaka Noe Jan 23 '19 at 17:41
  • Furthermore, if I call `Baby.setListener()` from somewhere else it will ask me for a `OnSadListener` and not a `OnCryingListener`... – Iaka Noe Jan 23 '19 at 17:44
  • @IakaNoe Your `Baby` class originally typed it as an `OnCryingListener`, so you would have needed to pass one in anyways. Since `OnSadListener ` is the parent, it is OK to use it as such when referring to a `Baby`. – Mr. Polywhirl Jan 23 '19 at 23:40
  • @IakaNoe You allowed to pass a sub-class into a method that expects its parent. This is called IOC (Inversion of Control). – Mr. Polywhirl Jan 23 '19 at 23:41
  • I know that. What I suspect is that I will lose the methods that the subclass implements by casting it to its parent type. – Iaka Noe Jan 24 '19 at 00:06
0

Fields are NOT "overridden" in Java. Only methods are overridden.

Since you assume that fields are overridden, in this example you are expecting only one listener field to exist in a Baby object. And you are expecting that single field to be set when you call michael.setListener().

But in reality, there are 2 fields in a Baby object. When you call michael.setListener(), only the field declared in Person class is set.

Prasad Karunagoda
  • 2,048
  • 2
  • 12
  • 16
  • I already knew that, yet I'm asking if there is a way to **achieve that same result**: having only one `listener` in a `Baby` object and it being set by `Baby.setListener()`. – Iaka Noe Jan 23 '19 at 17:47
  • Yes, you can achieve this by simply removing the `listener` field from `Baby`. And only have a `listener` field in `Person`. – Prasad Karunagoda Jan 24 '19 at 00:55