views:

921

answers:

2

I have made a generic Observer interface and an Observable class, but can't compile my class due to some generics problem. I don't know precisely why what I'm trying to do is forbidden. The code follows:

public class Observable<U> {

    private List<Observer<Observable<U>, U>> _observers = 
            new ArrayList<Observer<Observable<U>, U>>();

    public void addObserver(Observer<? extends Observable<U>, U> obs) {
        if (obs == null) {
            throw new IllegalArgumentException();
        }
        if (_observers.contains(obs)) {
           return;
        }
        _observers.add(obs); // This line does not compile
    }

    public void notifyObservers(U data) {
        for (Observer<? extends Observable<U>, U> obs : _observers) {
            // After correction of the list declaration, this line will not compile
            obs.update(this, data); 
        }        
    }
}

interface Observer<T, U> {
    public void update(T entity, U arg);
}
+5  A: 

Change your _observers definition to this:

private List<Observer<? extends Observable<U>, U>> _observers = 
        new ArrayList<Observer<? extends Observable<U>, U>>();

If you want to allow sublclasses you need to specify this in the declaration, not just in the place you're using it

MrWiggles
Now that I look at the code, that's probably the problem.
Thomas Owens
That solves the problem, but is there a reason I should specify that in the declaraton also?That correction caused another problem in other part of the code. I will edit the question and post everything.
Morgaelyn
+1  A: 

Try this:

 public class Observable<U> {

        private List<Observer<Observable<U>, U>> _observers =
                new ArrayList<Observer<Observable<U>, U>>();

        public void addObserver(Observer<Observable<U>, U> obs) {
            if (obs == null) {
                throw new IllegalArgumentException();
            }
            if (_observers.contains(obs)) {
               return;
            }
            _observers.add(obs);            }

        public void notifyObservers(U data) {
            for (Observer<? super Observable<U>, U> obs : _observers) {
                obs.atualizar(this, data);
            }
        }
    }

    interface Observer<T, U> {
        public void atualizar(T entity, U arg);
    }

To explain the underlying problem here, generics are forcing an explicit downcast. So you can't take any 'Observable' for any implementation of U and put it into a collection, because that collection is defined as taking a specific type of U, not anything.

For this type of use case, generics have limits, and you may not be able to accomplish what you want in such a type safe way.

EDIT: Would this work for you?

public class Observable<U> {

       private List<Observer<U>> _observers =
               new ArrayList<Observer<U>>();

       public void addObserver(Observer<U> obs) {
           if (obs == null) {
               throw new IllegalArgumentException();
           }
           if (_observers.contains(obs)) {
              return;
           }
           _observers.add(obs);            }

       public void notifyObservers(U data) {
           for (Observer<U> obs : _observers) {
               obs.atualizar(this, data);
           }
       }
   }

   interface Observer<U> {
       public void atualizar(Observable<U> entity, U arg);
   }
Yishai
This solves the problem at method notifyObservers(), but the problem ate method addObserver() remains.
Morgaelyn
Error message: The method add(Observer<Observable<U>,U>) in the type List<Observer<Observable<U>,U>> is not applicable for the arguments (Observer<capture#3-of ? extends Observable<U>,U>)
Morgaelyn
In what I posted there is no more extends. Did you change the other method signature as well?
Yishai
I tried again your code and it does compile. Unfortunately, I extended Observable and I need to "observe" such subclasses, so the signature of addObserver() needs to accept and Observer that can watch over subclasses of Observable.
Morgaelyn
But it is it always for the same type of U?
Yishai
I created a class "Request extends Observable<RequestEvent>" and a class "RequestEvent". I need then to create an "Observer<Request, RequestEvent>". Well, I wanted that the class Observer had no knowledge of the Observable class, but if this is not possible, then I guess it's a reasonable solution. I just need to be able to use observers that watch subclasses of Observable.
Morgaelyn
What do you do in the update method if you don't know the superclass of either object? I would say knowing about the Observable is unavoidable, but you could make it an interface to loosen the coupling further.
Yishai