Reputation: 65
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:
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);
}
Upvotes: 0
Views: 369
Reputation: 140318
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>
.
Upvotes: 2