Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get around this java generics overuse or misuse

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?

like image 662
rediVider Avatar asked Mar 05 '26 01:03

rediVider


1 Answers

@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.

like image 71
Paul Bellora Avatar answered Mar 07 '26 15:03

Paul Bellora



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!