1

I'm trying to implement Observable pattern with an abstract class (so that my subclasses don't provide a common implementation). I want polymorphic functions for different Observer types.

I have an interface IObservable:

public interface IObservable { /* also used version IObservable<O> */ <O> void registerObserver(O o); <O> void removeObserver(O o); } 

I wrote abstract class ObservableBase:

public abstract class ObservableBase implements IObservable { protected abstract <O> Set<O> getCollection(O o); @Override public <O> void registerObserver(O o) { getCollection(o).add(o); } @Override public <O> void removeObserver(O o) { getCollection(o).remove(o); } } 

And I want my concrete class to implement IObservable for different types of Observers:

// This is what it'd look like if I just implemented it by hand for generic interface public ConcreteClass implements IObservable<Observer1>, IObservable<Observer2> { @Override public void registerObserver(Observer1 o) {} @Override public void registerObserver(Observer2 o) {} @Override public void removeObserver(Observer1 o) {} @Override public void removeObserver(Observer2 o) {} } 

My idea is to put register/remove functionality in the base abstract class and provide only collection for this operations. But I cant wrap my hand around this. If I declare in the ConcreteClass specific type for getCollection, compiler complains "Method does not override method from its superclass".

Something like:

public ConcreteClass extends ObservableBase { protected Set<Observer1> getCollection(Observer1 o) {/*...*/} protected Set<Observer2> getCollection(Observer2 o) {/*...*/} } 

Is it possible?

3
  • 1
    But how do you plan to override getCollection in ConcreteClass twice? You can have multiple ConcreteClass implementations that override getCollection for different observers. Does that suit you? Commented Jul 23 at 21:28
  • 2
    The usage of generics here is seemingly confused. For instance, in the methods defined by IObservable, the type parameters are method scoped. So, since there are no constraints on those parameters, each method may as well accept Object no matter what the type parameter on IObservable is. This breaks the inheritance model you seem to be aiming for. Commented Jul 23 at 21:48
  • @GeorgiiLvov, I hoped to do this with overloading: getCollection(Observer1 o); getCollection(Observer2 o);. Or maybe to have one method ``getCollection(Class<T> o)`and then inside the function return proper set for provided type. Commented Jul 24 at 7:18

1 Answer 1

3

First, I will recreate what I believe to be the intended class hierarchy with the correct syntax. This is to eliminate any confusion as I attempt to answer your question.


Is it possible?

No, you cannot have some class which implements both Observable<Observer1> and Observable<Observer2>. See this other stack overflow question. Ultimately, those generic methods would have the same signature and clash, as the signature in this case is defined by the type bounds.

You can, however, achieve something similar with a different AbstractObservable implementation:

public abstract class AbstractObservable<O> implements Observable<O> { private final Set<Class<?>> allowedTypes; private final Set<O> observers; @SafeVarargs protected AbstractObservable(Class<? extends O>... allowedTypes) { this.allowedTypes = Set.of(allowedTypes); this.observers = new HashSet<>(); } // @Override public void registerObserver(O observer) { this.checkType(observer); this.observers.add(observer); } @Override public void unregisterObserver(O observer) { this.checkType(observer); this.observers.remove(observer); } private void checkType(Object o) { for (Class<?> allowed : this.allowedTypes) { if (allowed.isInstance(o)) return; } throw new IllegalArgumentException( "Observer of type " + o.getClass().getName() + " is not permitted"); } } 

Now you can declare observables with more complex type constraints:

public class GenericObservable extends AbstractObservable<Object> { public GenericObservable() { super(Object.class); } } public class FooAndBarObservable extends AbstractObservable<Object> // ^ // Instead of Object, this can be another common supertype of Foo and Bar { public FooAndBarObservable() { super(Foo.class, Bar.class); } } 

Personally, I do not see the motivation. I see 2 reasonable solutions to this issue:

  • The type constraint on the registerObserver/removeObserver methods is weakened, perhaps these methods can simply accept some Observer type that is wide enough to include the desired objects.

  • The type strength is maintained, however each Observable impl can truly only hold 1 common supertype of observer. If an Observable<Observer> can be constructed in any way, it becomes identical to the previous solution. With this system you can have selective Observables, but this condition is always inheritance-based. To only allow N specific Observer types, you would need to isolate a common supertype which is only extended/implemented by those N Observer types.

Sign up to request clarification or add additional context in comments.

3 Comments

"Is it possible? NO" and it makes no sense as well.
Why do you say that it makes no sense? For me it makes sense to have one common class which implements common functionality to spare me from writing all this "same" methods in a concrete class (or even in multiple concrete classes which implement Observable). I thought it's achievable because Java has overloading (so I can have multiple methods with the same name but different arguments) and common functionality is "generic" (in common sense, not developers - only collection is different).
Do not place parts of the answer on an external site. Include them in your answer. There is no recognizable reason for not including those few lines of code here.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.