This discussion is archived
12 Replies Latest reply: Mar 6, 2013 1:54 AM by 820145 RSS

Generics, oh dear...

820145 Newbie
Currently Being Moderated
I am writing an accounting system. The various windows need to listen for database changes so they know when to refresh their contents. So, they are DatabaseChangeListeners:
public interface DatabaseChangedListener extends EventListener
{
  public void databaseChanged(DatabaseChangedEvent e);
}
DatabaseChangedEvent is an abstract superclass of a series of classes, thus:
abstract public class DatabaseChangedEvent
{...}

public class AccountsChangedEvent extends DatabaseChangedEvent
{...}

public class CurrenciesChangedEvent extends DatabaseChangedEvent
{...}
etc


A typical window listens for more than one such type of event; e.g. a window which displays the details about an account, including its transactions, needs to know when the ACCOUNTS database table has changed, but also the TRANSACTIONS and CURRENCIES tables.

I would like my windows (DatabaseChangedListeners) to be able to do the following:
  @Override
  public void databaseChanged(AccountsChangedEvent e)
  {
    doStuffRelatedToAccounts();
  }

  @Override
  public void databaseChanged(CurrenciesChangedEvent e)
  {
    doStuffRelatedToCurrencies();
  }

  @Override
  public void databaseChanged(TransactionsChangedEvent e)
  {
    doStuffRelatedToTransactions();
  }
Of course, this doesn't compile. So I have tried declaring databaseChanged() in the interface as
  public void databaseChanged(<? extends DatabaseChangedEvent> e);
but I get told "wildcards may only be used as reference parameters". I change the question mark to a T, and I get "cannot resolve symbol T".

Is it possible to do what I want? (I really hope so; otherwise the only solution I can see is to use instanceof everywhere -- ugh) If so, what magic incantation do I need to use?

Many TIA!
  • 1. Re: Generics, oh dear...
    EJP Guru
    Currently Being Moderated
    You need
    public <T extends DatabaseListener> void databaseChanged(T event)
    to fix the syntax error, but I don't see that genericizing the actual methods is going to get you anywhere, as it doesn't really help the caller. I think you need to genericize the interface and have the relevant classes implement the appropriate one for themselves:
    public interface DatabaseChangedListener<T extends DatabaseChangedEvent> extends EventListener
    {
      public void databaseChanged(T e);
    }
  • 2. Re: Generics, oh dear...
    820145 Newbie
    Currently Being Moderated
    Thanks EJP, I've done what you suggest. The interface is now declared as in your post. But now I have another problem! :-[

    I assume that my window classes now need to each have one or more (usually more) "implements" clauses in their top line, saying that they implement the interface in each "form" appropriate? e.g. if a window needs to listen for ACCOUNTS, CURRENCIES and TRANSACTIONS change events, then its top line should look like this:
    public class AWindow extends MyWindow implements DatabaseChangedListener<AccountsChangedEvent>, 
    DatabaseChangedListener<CurrenciesChangedEvent>, DatabaseChangedListener<TransactionsChangedEvent>
    But this doesn't compile! I get "Duplicate class: DatabaseChangedListener".

    Did I assume wrong?
  • 3. Re: Generics, oh dear...
    gimbal2 Guru
    Currently Being Moderated
    So where did you read that it was possible to implement the same interface multiple times?
  • 4. Re: Generics, oh dear...
    820145 Newbie
    Currently Being Moderated
    Gimbal, thanks for your "helpful" post. I haven't read it anywhere. But being able to have multiple, polymorphic versions of my method databaseChanged() is necessary in order to avoid having to use some mechanism like instanceof to respond appropriately to the various possible events that will be fired to the listeners. Furthermore, when I changed the top line of my window class, from
    public class AWindow extends MyWindow implements DatabaseChangedListener
    to
    public class AWindow extends MyWindow implements DatabaseChangedListener<AccountsChangedEvent>
    only the version of databaseChanged() which takes an AccountsChangedEvent stopped having an error signalled on it. This didn't surprise me, since I hadn't yet "introduced" to the compiler the idea that the window class also is a DatabaseChangedListener for e.g. CurrenciesChangedEvents. I therefore went ahead and added
    DatabaseChangedListener<CurrenciesChangedEvent>
    to the top line of my window class. And then the error that I spoke of in my second post was signalled.

    Do you happen to have any ideas as to how I can achieve what I need to?
  • 5. Re: Generics, oh dear...
    TPD-Opitz-Consulting-com Expert
    Currently Being Moderated
    The point is:

    You should not have top level classes implement "Listener"-interfaces at all (accept the class represents nothing else than the listener)

    Create Listener instances as (anonymous) inner classes.

    bye
    TPD
  • 6. Re: Generics, oh dear...
    gimbal2 Guru
    Currently Being Moderated
    TPD Opitz-Consulting com wrote:
    Create Listener instances as (anonymous) inner classes.
    That would be the easiest way yes, unless you have like a dozen of them.
  • 7. Re: Generics, oh dear...
    TPD-Opitz-Consulting-com Expert
    Currently Being Moderated
    gimbal2 wrote:
    TPD Opitz-Consulting com wrote:
    Create Listener instances as (anonymous) inner classes.
    That would be the easiest way yes, unless you have like a dozen of them.
    What exacly is the problem with that?
    If that dozen Listeners implement the same Interface doing the same on different objects I create a (non anonymous) inner class with a constructor parameter.

    If I have dozen different Listener interfaces to implement my class may be to meighty...

    bye
    TPD
  • 8. Re: Generics, oh dear...
    820145 Newbie
    Currently Being Moderated
    Thanks TPD, for the final piece of the answer. Yes, I should have thought of that myself, but I'm just back to Java coding after a multi-month break from coding due to a tragic event in my life last year. I'm rustier than I would like, both with my codebase and with Java in general. To make matters worse, I have never managed to find a treatment of Java generics that I could get my head around. My mentor, who in his day was a seriously heavy hitter in the industry (and who hasn't been able to work for years -- that's what we get for a youth-obsessed industry and culture in general), says that even he finds the material on generics "dense". (And yes, he has done some pretty cool stuff in Java, so he's not a newbie to it.)

    If anyone out there knows of a "gentle" treatment on generics, I'd be grateful for any pointers. I don't like having to ask for help and would far prefer to solve my problems myself.
  • 9. Re: Generics, oh dear...
    TPD-Opitz-Consulting-com Expert
    Currently Being Moderated
    Do not excuse!

    There is no reason to be ashamed because of mistakes and a lag of knowledge as long as you're willing to change that...
    ;o)

    bye
    TPD
  • 10. Re: Generics, oh dear...
    gimbal2 Guru
    Currently Being Moderated
    TPD Opitz-Consulting com wrote:
    Do not excuse!

    There is no reason to be ashamed because of mistakes and a lag of knowledge as long as you're willing to change that...
    ;o)
    Highly agreed. Making mistakes is a good thing too, there is no better teacher. But you need to attack that with the right attitude; don't start feeling down or ashamed, learn and grow from it. Woohoo, you learned a little!
  • 11. Re: Generics, oh dear...
    r035198x Pro
    Currently Being Moderated
    If this is a sizeable project then prefer Actions to listeners.
  • 12. Re: Generics, oh dear...
    820145 Newbie
    Currently Being Moderated
    Thanks guys, yes indeed I have learned some from this. In the absence of a "gentle" treatment on generics, I am forming -- gradually -- a kind of "cookbook" for generics, at least that way I have more ideas to try, at my fingertips.

Legend

  • Correct Answers - 10 points
  • Helpful Answers - 5 points