Search code examples
javagenericsdesign-patternsobserver-patterntype-safety

Type safe generic Java observer compile time error


Requirements

I am trying to write an Observer / Observable pair of classes. I want to parametrize the Observer so that a type safe update call is possible. Imagine this version:

class View implements Observer<Model> {
    @Override
    public void update(Model model) { render(model); }  // no casting:)
}

Instead of this version that needs casting:

class View implements Observer {
    @Override
    public void update(Object model) { render((Model) model); }  // casting:(
}

Attempt

Here's what I have so far. My Observer interface:

public interface Observer<T extends Observable> {
    public void update(T observable);
}

and my Observable abstract class:

import java.util.List;

public abstract class Observable {
    private List<Observer<? extends Observable>> observers;

    public Observable() {
        System.out.println(this.getClass());
    }

    public void addObserver(Observer<? extends Observable> obs) {
        observers.add(obs);
    }

    public void removeObserver(Observer<? extends Observable> obs) {
        observers.remove(obs);
    }

    protected <E extends Observable> void updateObservers(E self) {
        for (Observer<? extends Observable> obs : observers) {
            obs.update(self);  // <-------------------- COMPILER ERROR!!!
        }
    }
}

Problem

The line labeled "COMPILER ERROR" has a problem with the .update():

The method update(capture#4-of ? extends Observable) in the type Observer is not applicable for the arguments (E)

So even though the self argument passed to update() is of type E extends Observable, it does not satisfy the interface method signature update(T observable); where T extends Observable. Why is that? I really expected those to be compatible.

Is there I can fix this to meet my requirements?

Thanks.


Solution

  • Why is that? I really expected those to be compatible.

    Because the function public void update(T observable); has the same generic argument T as the Observer<T extends Observable> class, which means that it's argument type should be the same as the type of the reference variable which will invoke the update() function.

    While, when you try to use the wildcard ? then the type of the variable obs is Observer<? extends Observable> which could be any different class that extends Observable than the type of the method arguments which is E.

    Is there I can fix this to meet my requirements?

    Yes, using the Self Bound / Recursive Generics, a concrete example is the Class Enum<E extends Enum<E>>, to understand this concept you can take a look at the See also section below.

    So, your Observable will be something like:

    import java.util.List;
    
    public abstract class Observable<T extends Observable<T>> {
        private List<Observer<T>> observers;
    
        public Observable() {
            System.out.println(this.getClass());
        }
    
        public void addObserver(Observer<T> obs) {
            observers.add(obs);
        }
    
        public void removeObserver(Observer<T> obs) {
            observers.remove(obs);
        }
    
        protected  void updateObservers(T self) {
            for (Observer<T> obs : observers) {
                obs.update(self);  
            }
        }
    }
    

    The Observer interface:

    public interface Observer<T extends Observable<T>> {
        public void update(T observable);
    }
    

    The View class:

    class View implements Observer<Model> {
        @Override
        public void update(Model model) { render(model); } 
    }
    

    Assuming that your Model class should be something like this:

    public class Model extends Observable<Model>{
    
    }
    

    See also: