Reputation: 4609
I know there are many (!) Q/A on this general topic; but haven't yet been able to resolve my specific approach.
I adapted this code from a Scala example by Odersky, which shows mutually recursive generic types to show a bi-directional link ability between Observer & Observed.
In Subject::publish() when I broadcast the event to all observers, even though my type is Subject<S,O>, and the target Observer is of type <S,O> and thus also its notify method; I get a type error,
The method notify(S) in the type Observer<S,O> is not applicable for the arguments (Subject<S,O>)
unless I explicitly cast the passed argument to notify().
obs.notify( (S) this ); The types seem right, but the compiler disagrees(!).
May be something silly, but it eludes me. TIA
abstract class Subject< S extends Subject< S, O >,
O extends Observer< S, O > > {
private final List< O > observers = new ArrayList<O>();
void subscribe( O obs ) { observers.add( obs ); }
void publish() {
for ( final O obs : observers )
// TThe method notify(S) in the type Observer<S,O> is not applicable for the arguments (Subject<S,O>)
obs.notify( this ); // (S)this ??
}
}
//-------------------------------------------------------------
abstract class Observer< S extends Subject< S, O >,
O extends Observer< S, O > > {
abstract void notify( S sub );
}
//-------------------------------------------------------------
class Sensor extends Subject< Sensor, Display > {
double value = 0.0;
String name = "";
Sensor(String nm) { name = nm; }
void changeValue( final double v ) {
value = v;
publish();
}
public String toString() { return "Sensor:" + name; }
}
class Display extends Observer< Sensor, Display > {
void notify( Sensor sub ) { // Note typed argument!
System.out.println( sub + " has value " + sub.value );
}
}
//-------------------------------------------------------------
public class SubjectObserver {
public static void main( final String[] notUsed ) {
final Display o = new Display();
final Sensor s = new Sensor("Temperature");
s.subscribe( o );
s.changeValue( 1 );
}
}
Upvotes: 1
Views: 178
Reputation: 468
You've almost got it right!
The self-bounded (or recursive) type declarations imply a several subtle nuances. Given your observer declaration we can state that:
abstract class Observer<S extends Subject<S, O>, O extends Observer<S, O>> {
abstract void notify(S sub);
}
<S extends Subject<S, O>
makes sure that only subtypes of type Subject<S, O>
are permitted as type arguments,
meaning that notify(S sub)
cannot accept the Subject
class itself, but only its descendants.Child extends Subject<Child, O>
meaning a subtype such as Sensor extends Subject<NotASensorItself, O>
would be impossible (which is what you actually want)Speaking of the Subject#publish
method, by using THIS
you're effectively forcing an observer to accept the Subject
(the parent)
which does not conform the declared bound (S
≠ Subject<S, O>
) hence you got the error:
void publish() {
for (final O obs : observers)
obs.notify(this); <<< Here only S are acceptable
}
when in fact you wanted the obs.notify(XXX)
to accept a Clild instance and one of the options to do so - is to pass that reference along with the method invocation from a Clild directly:
void publish(S subject) {
for (final O obs : observers)
obs.notify(subject);
}
And the Sensor implementation:
class Sensor extends Subject<Sensor, Display> {
...
void changeValue(final double v) {
value = v;
publish(this); << Here it passes itself, so now it conforms the bound for `S`
}
}
A couple of links that might be helpful to grok mind-bending recursive types:
Upvotes: 1