45

I have an abstract Class Monitor.java which is subclassed by a Class EmailMonitor.java.

The method:

public abstract List<? extends MonitorAccount> performMonitor(List<? extends MonitorAccount> accounts)

is defined in Monitor.java and must be overridden in EmailMonitor.java.

I currently have the method overridden in EmailMonitor.java as follows:

@Override
public List<EmailAccount> performMonitor(List<EmailAccount> emailAccounts) {
    //...unrelated logic
    return emailAccounts;
}

However, this produces the compile time error:

Name clash: The method performMonitor(List<EmailAccount>) of type EmailMonitor has the same erasure as performMonitor(Lis<? extends MonitorAccount> emailAccounts) of type Monitor but does not override it

EmailAccount is a subclass of MonitorAccount, so (in my mind at least) overriding it in this way makes perfect sense. Seeing as the compiler is not happy with my logic though, How should I go about this correctly while still keeping my compile time checks to make sure that all calls to EmailMonitor.performMonitor() receive Lists of EmailAccount rather than some other type of MonitorAccount?

reevesy
  • 3,452
  • 1
  • 26
  • 23
Robert Ngetich
  • 1,114
  • 2
  • 11
  • 22

2 Answers2

38

No, it's not overriding it properly. Overriding means you should be able to cope with any valid input to the base class. Consider what would happen if a client did this:

Monitor x = new EmailMonitor();
List<NonEmailAccount> nonEmailAccounts = ...;
x.performMonitor(nonEmailAccounts);

There's nothing in there which should give a compile-time error given your description - but it's clearly wrong.

It sounds to me like Monitor should be generic in the type of account it can monitor, so your EmailMonitor should extend Monitor<EmailAccount>. So:

public abstract class Monitor<T extends MonitorAccount>
{
    ...
    public abstract List<? extends T> performMonitor(
        List<? extends T> accounts);
}

public class EmailMonitor extends Monitor<EmailAccount>
{
    @Override
    public abstract List<? extends EmailAccount> performMonitor(
        List<? extends EmailAccount> accounts)
    {
        // Code goes here
    }
}

You might want to think carefully about the generics in the performMonitor call though - what's the return value meant to signify?

Amal K
  • 4,359
  • 2
  • 22
  • 44
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 1
    You say 'List extends EmailAccount>'. Now I can't pass EmailAccount through at all; Was that a typo?. I wanted to check that EmailMonitor always receives a list of EmailAccount and always returns a list of EmailAccount that can be used without dynamic casting. posting my solution now :) – Robert Ngetich Oct 27 '08 at 14:38
11

Here is my own solution. I suspect this is the same thing Jon Skeet was trying to get at... without the typo (see my comment in reply to his answer).

the Monitor.java class:

public abstract class Monitor <T extends MonitorAccount> {
  ...
  public abstract List<T> performMonitor(List<T> accounts);
  ..
}

EmailMonitor.java

public class EmailMonitor extends Monitor<EmailAccount> {
  ...
  public List<EmailAccount> performMonitor(List<EmailAccount> emailAccounts) {
    ..//logic...logic...logic
    return emailAccounts;
  }
  ...
}

In this configuration, EmailMonitor.performMonitor() will always check at compile time that it receives a list of EmailAccount rather than any of my other types FTPAccount, DBAccount, etc... It's much cleaner than the alternative, which would have been receiving/sending a raw list and then having to coerce it the required type resulting in potential runtime type casting exceptions.

Thomas Eizinger
  • 1,404
  • 14
  • 25
Robert Ngetich
  • 1,114
  • 2
  • 11
  • 22