Reputation: 415
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?
My observer is declared as interface like this:
public interface Observer<T> {
void update(T subject);
}
Upvotes: 0
Views: 471
Reputation: 2837
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 pass this
to observers; and you also want for this
to be of type T
.
At that point, this
is not guaranteed to be of type T
of course- it is of type Subject. But "this" will be of type T in a concrete Subject
class. So substitute this
with getThisSubject()
and move it down in the hierarchy. In code:
package stackOv;
import java.util.*;
abstract class Subject<T extends Subject<T>> {
private List<Observer<T>> observers = new ArrayList<>();
// returns a reference of this, of type T
protected abstract T getThisSubject();
protected void doNotify() {
for(Observer<T> observer : observers) {
observer.update(getThisSubject());
}
}
public void addObserver(Observer<T> obs) {
observers.add(obs);
}
}
class SubjectA extends Subject<SubjectA> {
@Override
protected SubjectA getThisSubject() {
return this;
}
}
interface Observer<T> {
void update(T subject);
}
class UseSubject {
public static void main(String[] args) {
SubjectA sub = new SubjectA();
Observer<SubjectA> obs = new Observer<SubjectA>() {
@Override
public void update(SubjectA subject) {
//and here we have a reference to the right type
System.out.println("subj=" + subject);
}
};
sub.addObserver(obs);
sub.doNotify();
}
}
Let me stress that we are coupling Observer
types with Subject
types; they really are in a one-to-one relationship. A way to avoid this is declaring Subject
non-generic, and
interface Observer {
void update(Subject subject);
}
plus using visitor or other patterns. (but this reply is long enough already).
Upvotes: 1