I'm trying to create a generic boradcaster to which client code may subscribe. Client code will then be updated (via the ReportListener) when any changes are mode to concrete Reporter_Abstract sub classes.
I know I'm over using generics here (for example
public abstract class Reporter_Abstract<I extends Reporter_Abstract<I>>
feels dirty), but i'm not sure how to enforce these requirements in another way. How can i ensure that a listener added in code handles changes to I, where I is the subtype of Reporter_Abstract
public abstract class Reporter_Abstract<I extends Reporter_Abstract<I>>
{
private final List<ReportListener<I>> _listeners;
/**
* Broadcast change to any attached listeners, skipping the sender
*/
protected final void reportChange( ReportListener<I> sender )
{
List<ReportListener<I>> listeners = collectListeners();
for( ReportListener<I> listener : listeners )
{
try
{
if( sender == null || sender != listener )
{
listener.recognizeChange( (I) this );
}
}
catch( Exception e )
{
_log.error( "Uncaught exception from listener: " + listener, e );
}
}
}
}
public class SomeStateReporter extends Reporter_Abstract<SomeStateReporter>
{
private boolean _isSomeStateActive;
public void setSomeStateActive( boolean isSomeStateActive )
{
if( isSomeStateActive ^ _isSomeStateActive )
{
_isASomeStateActive = isSomeStateActive;
super.reportChange();
}
}
}
public interface ReportListener<T extends Reporter_Abstract<?>>
{
public void recognizeChange( T report );
}
And the class that wants to listen to changes
public class ChangeHandlingClass()
{
public void attachSomeStateListener( SomeStateReporter someStateReporter )
{
sometateReporter.addListener( new SomeStateUpdateListener() );
}
private class SomeStateUpdateListener implements ReportListener<SomeStateReporter>
{
@Override
public void recognizeChange( SomeStateReporter report )
{
handleStateChange( report.isSomeStateActive()
}
}
}
If this is the right way (or A right way), then shouldn't this line
listener.recognizeChange( (I) this );
allow me to use an argument of 'this' without the cast, and know that this is the definition of I?
Am I way off base?
@StriplingWarrior and I discussed a similar pattern on this post: Is there a way to refer to the current type with a type variable?
I would say that you're not off track in terms of implementing this pattern, however it's important to recognize the contract that Reporter_Abstract must lay out to its subclasses, which are responsible for implementing it correctly. Subclasses must be final as long as they consume the type parameter I with their own type, to prevent additional subclasses from making I incorrect.
Enum employs a similar pattern - an additional reason why an enum cannot be extended, because its parent class Enum expects its type parameter E to match the type of the extending enum.
The real question is whether you want this pattern as opposed to something like @Andy suggests. What you're trying to do can only be done safely if you have exclusive control over anything subclassing Reporter_Abstract.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With