0

I implemented a simple observer pattern using generics in Java.

Anyway, now the example works only because the update method of the ConcreteObserver is implemented with the .toString() method that is common to all objects.

In a real world case, the ConcreteObserver only operates on a specific type of data. For example, if my ConcreteObserver is a player, it can plays .mp3 or .avi but not .doc.

Which is the correct way to "force" a specific kind of data to be passed as a contract between Observer and Observable? Generics is too generic...

In the case of method signatures, I can use Interfaces to force the implementation of a specific signature.

Which is the way I can force a specific data type passed?

For example: I want a kind of data composed of:

  • a string
  • an int (the length of the next binary data block)
  • a a block of binary data
  • some kind of metadata descriptor of the binary block

I want this kind of data interface the only allowed.


Here is the code where I use generics.

public interface Observable<T> {
    public void add(Observer<T> observer);
    public void remove(Observer<T> observer);
    public void sendNotify(T notification);
}

public class ConcreteObservable<T> implements Observable<T> {

    ArrayList<Observer<T>> observerList = new ArrayList<>();

    public void add(Observer<T> observer){
        observerList.add(observer);
    }

    public void remove(Observer<T> observer) {
        observerList.remove(observer);
    }

    public void sendNotify(T notification) {
        for (Observer observer : observerList) {
            observer.update(notification);
        }
    }
}


public interface Observer<T> {
    public void update(T value);
}

public class ConcreteObserver<T> implements Observer<T> {

    @Override
    public void update(T value) {
        System.out.println(value.toString()); 
    }
}

public static void main(String[] args) {

    ConcreteObservable observable = new ConcreteObservable();
    ConcreteObserver observer = new ConcreteObserver();
    observable.add(observer);

    int value = 5;
    observable.sendNotify(value);

    String string = "Test";
    observable.sendNotify(string);      

}

Pich
  • 61
  • 9
  • 3
    `public class Mp3Observer implements Observer`? – chrylis -cautiouslyoptimistic- Mar 09 '20 at 23:05
  • 1
    look this here maybe useful [generic constraints](https://stackoverflow.com/questions/2081663/how-to-set-constraints-on-generic-types-in-java) – Rans Mar 09 '20 at 23:11
  • 1
    As per the first comment: once you get to the "your actual own code" level, things stop being `T` and `super` etc. You write your real code as implementations of those classes and interfaces with generics, specifying which real classes/interfaces are used by your code. – Mike 'Pomax' Kamermans Mar 09 '20 at 23:15
  • Are you certain you want an observer pattern? Your reference to observers operating on specific types of data make me suspicious that you are really after a dispatcher that notifies different observers depending on the type of data. The observer pattern generally refers to situations in which all observers are notified about a change of state. – sprinter Mar 09 '20 at 23:36
  • @sprinter I want to communicate to a group of observers a change in a set of objects that are similar. My goal is not to send different notification to different receiver based on the message type. Thank you anyway for your comment. – Pich Mar 10 '20 at 22:40

1 Answers1

2
ConcreteObservable observable = new ConcreteObservable();
ConcreteObserver observer = new ConcreteObserver();

The problem here is that you're using raw types: you've omitted the type parameters on observer and observable.

Add type parameters. For example, if you specify Integer, you won't be able to pass a String parameter:

ConcreteObservable<Integer> observable = new ConcreteObservable<>();
ConcreteObserver<Integer> observer = new ConcreteObserver<>();

observable.add(observer);       // Fine.

int value = 5;
observable.sendNotify(value);   // Fine.

String string = "Test";
observable.sendNotify(string);  // Error!

Your IDE or compile will give you warnings about the use of raw types: pay attention to them (there are other raw types in your code).


Also, note that you can use Observer<? super T> instead of Observer<T> in your Observable<T> interface and its implementations. This just makes the API a little bit more flexible, so you can use, say, an Observer<Object> (which will accept an Integer parameter to sendNotify) rather than just specifically an Observer<Integer>.

Andy Turner
  • 137,514
  • 11
  • 162
  • 243
  • Thank you for your answer. Following your indication I found that I was searching for Bounded Type parameters: https://docs.oracle.com/javase/tutorial/java/generics/bounded.html where your `Observer super T>` is what I'm searching: **generics, but not too much.** Also the error using the raw types: not writing explicitely the type indication drove me to a confusion. – Pich Mar 10 '20 at 22:33
  • 1
    @Pich still, bounds mean nothing if you use raw types. – Andy Turner Mar 11 '20 at 08:00