0

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);
}
user2546926
  • 385
  • 3
  • 12
  • In your case, your `observer` is a consumer, therefore you should use `super`: `private List> observers = ...;` – Turing85 Jun 10 '18 at 08:53
  • Possible duplicate of [What is PECS (Producer Extends Consumer Super)?](https://stackoverflow.com/questions/2723397/what-is-pecs-producer-extends-consumer-super) – Turing85 Jun 10 '18 at 08:55
  • I don't think so. The post you are referring to is about how to accept a collection as function parameter. I am not looking on how to accept a collection, but rather to provide a pointer to `this` to each item in a collection. The problem I am trying to solve is the cast from `this` to the underlying child class (i.e. `return (T)this`). – user2546926 Jun 10 '18 at 09:12
  • You can extrapolate the meaning from the example: If a generic class is a producer, use `extends` on the generic type. If a generic class is a consumer, use `super` on the generic type. Your `observer` is a consumer. Therefore, use `super`. – Turing85 Jun 10 '18 at 09:14
  • How about `class Subject>`? – Andy Turner Jun 10 '18 at 09:23
  • Oh you are right, the exact definition is a little more complex: `private List>> observers = new ArrayList<>();` (`T` itself is not sufficient) (plus what @AndyTurner said, since you are basically using raw types right now) – Turing85 Jun 10 '18 at 09:24
  • @AndyTurner this alone is not sufficient to get rid of the warning. – Turing85 Jun 10 '18 at 09:28
  • @Turing85 that is true. But there isn't actually a way to get rid of the warning, because there is nothing to constrain the class to be exactly self-bounded. – Andy Turner Jun 10 '18 at 09:29
  • @AndyTurner you can constrain `observers`. But then again, without further knowledge of the `Observer` class/interface, I do not know whether my suggestion is feasible. – Turing85 Jun 10 '18 at 09:30
  • @Turing85 it has nothing to do with `Observer` itself. It's like the fact that you can write a class `class SomeClass implements Comparable`: it doesn't make sense to do this directly, but it doesn't mean you *can't*. – Andy Turner Jun 10 '18 at 09:34
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/172836/discussion-between-turing85-and-andy-turner). – Turing85 Jun 10 '18 at 09:36
  • @AndyTurner I tried the `Subject>` and it did not resolve the warning. – user2546926 Jun 10 '18 at 09:37
  • I don't see any reason for your Subject to be generic. If what is passed to the observers is the subject itself, then just use a non-generic Subject class, and accept observers of type `Observer super Subject>`. – JB Nizet Jun 10 '18 at 10:15

1 Answers1

1

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

Daniele
  • 2,672
  • 1
  • 14
  • 20
  • I like this approach, its simple and effective. I dont really like the thought of implementing the `getThisSubject()` in all concrete subjects, but I also think that there is not a better alternative. – user2546926 Jun 11 '18 at 16:48
  • To respond on whether it's a good idea, you say you can't register at observers with other type, and that is just half true, I think. You could implment multiple observers with different types. Also, you say the types are coupled and that is true. However, observing a class of unknown or abstract type does not make sense in real life. Usually you are not really intereseted in if there was a change, but rather what the change was. Hence, if the `update` passes an abstract type Subject you still need a cast to the type you are observing before you can figure out what the change was. – user2546926 Jun 11 '18 at 16:57