web-dev-qa-db-fra.com

Remplacer une méthode avec des paramètres génériques en Java?

J'ai une classe abstraite Monitor.Java qui est sous-classée par une classe EmailMonitor.Java.

La méthode:

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

est défini dans Monitor.Java et doit être remplacé dans EmailMonitor.Java.

J'ai actuellement la méthode remplacée dans EmailMonitor.Java comme suit:

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

Cependant, cela produit l'erreur de temps de compilation:

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 est une sous-classe de MonitorAccount, donc (dans mon esprit au moins) le remplacer de cette manière est parfaitement logique. Étant donné que le compilateur n'est pas satisfait de ma logique, comment dois-je procéder correctement tout en conservant mes vérifications de temps de compilation pour m'assurer que tous les appels à EmailMonitor.performMonitor() reçoivent plutôt des listes de EmailAccount qu'un autre type de MonitorAccount?

43
Robert Ngetich

Non, ce n'est pas le remplacer correctement. Le remplacement signifie que vous devriez être capable de gérer toute entrée valide dans la classe de base. Considérez ce qui se passerait si un client faisait ceci:

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

Il n'y a rien là-dedans qui devrait donner une erreur de compilation compte tenu de votre description - mais c'est clairement faux.

Il me semble que Monitor devrait être générique dans le type de compte qu'il peut surveiller, donc votre EmailMonitor devrait s'étendre Monitor<EmailAccount>. Alors:

public abtract 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
    }
}

Vous voudrez peut-être bien réfléchir aux génériques de l'appel performMonitor - quelle est la valeur de retour signifiée?

35
Jon Skeet

Voici ma propre solution. Je soupçonne que c'est la même chose que Jon Skeet essayait de comprendre ... sans la faute de frappe (voir mon commentaire en réponse à sa réponse).

les Monitor.Java classe:

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;
  }
  ...
}

Dans cette configuration, EmailMonitor.performMonitor () vérifiera toujours au moment de la compilation qu'il reçoit une liste de Compte email plutôt que n'importe lequel de mes autres types FTPAccount, DBAccount, etc ... C'est beaucoup plus propre que l'alternative, qui aurait été de recevoir/envoyer une liste brute et ensuite de la contraindre au type requis, ce qui pourrait entraîner des exceptions de transtypage de type d'exécution.

10
Robert Ngetich