javagenericsdesign-patternsobserver-patternunchecked-cast

No cast for observer pattern using generics


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);
}

Solution

  • 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).