Reputation: 9453
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);
}
Upvotes: 4
Views: 4198
Reputation: 91881
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);
}
Upvotes: 3
Reputation: 21184
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
Upvotes: 6