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_Abstractmust lay out to its subclasses, which are responsible for implementing it correctly. Subclasses must befinalas long as they consume the type parameterIwith their own type, to prevent additional subclasses from makingIincorrect.Enum employs a similar pattern – an additional reason why an
enumcannot be extended, because its parent classEnumexpects its type parameterEto match the type of the extendingenum.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.