I was looking to implement an subject-observer pattern where the subject provides its self to the observers when notifying.
public class Subject<T extends Subject> {
/** suporting stuff for subject */
private List<Observer<T>> observers = new ArrayList<>();
protected void doNotify() {
for(Observer<T> observer : observers) {
/** This is the line where it gets interesting */
observer.update((T)this);
}
}
}
Practically, this work, however, the compiler gives a Unchecked cast warning on the observer.update((T)this);
line.
When reading a bit about this, the compiler is right (surprise surprise) and its even considered as smelly code, as you can write code that actually triggers the ClassCastException.
Now I am looking for a solution that isn't smelly and rock solid. However, the thought that an observer does not need to look for the subject that it is observing is something I really like. Also, I don't really like the observers need to do the cast themselves in their update()
. Do you have any suggestion on how to on this one?
Edit
My observer is declared as interface like this:
public interface Observer<T> {
void update(T subject);
}
Foreword: I would suggest NOT to use generics- because by forcing the clients (Observers) to know about the exact type of a Subject, (as opposed to having a non-generic Subject class), you cannot eg. subscribe the same Observer to multiple Subjects (of different types).
Anyway, there is a way to have type-safety .
In the call
observer.update((T)this)
you want two things: you want to passthis
to observers; and you also want forthis
to be of typeT
.At that point,
this
is not guaranteed to be of typeT
of course- it is of type Subject. But "this" will be of type T in a concreteSubject
class. So substitutethis
withgetThisSubject()
and move it down in the hierarchy. In code:Let me stress that we are coupling
Observer
types withSubject
types; they really are in a one-to-one relationship. A way to avoid this is declaringSubject
non-generic, andplus using visitor or other patterns. (but this reply is long enough already).