Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 916
kdbanman Avatar asked Feb 22 '15 01:02

kdbanman


People also ask

What can I use instead of Observer in Java?

So, what's the alternative we have ? You can use PropertyChangeEvent and PropertyChangeListener from java.

Why is Observer deprecated Java?

Deprecated. This class and the Observer interface have been deprecated. The event model supported by Observer and Observable is quite limited, the order of notifications delivered by Observable is unspecified, and state changes are not in one-for-one correspondence with notifications.

What is Observer methods in Java?

Observer is a behavioral design pattern that allows some objects to notify other objects about changes in their state. The Observer pattern provides a way to subscribe and unsubscribe to and from these events for any object that implements a subscriber interface.

Does Java have Observer?

The Java language supports the MVC architecture with two classes: Observer : Any object that wishes to be notified when the state of another object changes.


3 Answers

Use import io.reactivex.Observable;

instead of import java.util.Observable;

like image 91
user1555123 Avatar answered Oct 17 '22 22:10

user1555123


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:

  • Java Enum definition
  • Self-bounding generics
  • Java - Interface extending itself
  • What would be the use of accepting itself as type arguments in generics
like image 21
Tarik Avatar answered Oct 17 '22 21:10

Tarik


Problem

As per @hexafraction's commentary,

The Observable's List of the Observers is parametrized by any subclass of Observer (wildcard <? extends Observer>). This does not play nice with Observer<T extends Observable>'s update(T observable) method, which is expecting exactly the type of Observable which the Observer was parametrized by.

Here's a counterexample: class A extends Observable, class B extends Observable, class A1 extends A. The wildcard for obs would allow an Observer to be assigned to obs, and in that case calling obs.update() with an instance of A1 would be invalid.

Solution

The solution is to parametrize the Observable class as well, so that the List contents are constrained correctly for the Observer update() method:

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

    public Observable() {
        observers = new ArrayList<>();
    }

    public void addObserver(Observer<E> obs) {
        observers.add(obs);
    }

    public void removeObserver(Observer<E> obs) {
        observers.remove(obs);
    }

    protected void updateObservers(E self) {
        for (Observer<E> obs : observers) {
            obs.update(self);
        }
    }
}

And the Observer parametrized by its own T:

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

If you need type constraints such that Observers may only observe Observables, and/or Observables may only send Observables to Observer updates, then use these class signatures:

public interface Observer<T extends Observable<T>> { ... }
public abstract class Observable<E extends Observable<E>> { ... }

The class bodies and behaviors are unaffected, but the compiler will complain if Observers and Observables are bound to anything but each other.

Usage

Parametrize an Observer (View) by the Observable (Model):

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

Parametrize an Observable (Model) by itself and call updateObservers():

class Model extends Observable<Model> {
    public void doStuff() {
        //...
        updateObservers(this);
    }
}
like image 2
kdbanman Avatar answered Oct 17 '22 23:10

kdbanman