Pourquoi une classe abstraite implémentant une interface peut manquer la déclaration / implémentation de l'une des méthodes de l'interface?

123

Une chose curieuse se produit en Java lorsque vous utilisez une classe abstraite pour implémenter une interface: certaines méthodes de l'interface peuvent être complètement manquantes (c'est-à-dire qu'aucune déclaration abstraite ou implémentation réelle n'est présente), mais le compilateur ne se plaint pas.

Par exemple, étant donné l'interface:

public interface IAnything {
  void m1();
  void m2();
  void m3();
}

la classe abstraite suivante est joyeusement compilée sans avertissement ni erreur:

public abstract class AbstractThing implements IAnything {
  public void m1() {}
  public void m3() {}
}

Pouvez-vous expliquer pourquoi?

Giulio Piancastelli
la source
2
On ne peut pas créer un objet d'une classe abstraite. Ainsi, tant qu'une implémentation n'est pas fournie pour une classe abstraite, les objets ne peuvent pas être créés pour IAnything. C'est donc tout à fait correct pour le compilateur. Le compilateur s'attend à ce que toute classe non abstraite qui implémente IAnything doit implémenter toutes les méthodes déclarées en dehors de IAnything. Et comme il faut étendre et implémenter AbstractThing pour pouvoir créer des objets, le compilateur lancera une erreur, si cette implémentation n'implémente pas les méthodes de IAnything laissées de côté par AbstractThing.
VanagaS
J'avais une classe concrète qui étendait son propre "AbstractThing" dans un scénario identique à celui-ci, et même si je n'avais pas implémenté l'une des méthodes de l'interface, elle compilait inexplicablement. Maintenant, il fait ce à quoi je m'attendais, mais je ne peux pas comprendre ce qui le faisait réussir auparavant. Je soupçonne que je n'avais pas :wun des fichiers.
Braden Best
vous pouvez voir la réponse pour une question similaire stackoverflow.com/questions/8026580/…
Do Nhu Vy

Réponses:

156

En effet, si une classe est abstraite, vous devez par définition en créer des sous-classes pour l'instancier. Les sous-classes seront requises (par le compilateur) pour implémenter toutes les méthodes d'interface que la classe abstraite a laissées de côté.

En suivant votre exemple de code, essayez de créer une sous-classe de AbstractThingsans implémenter la m2méthode et voyez les erreurs que le compilateur vous donne. Cela vous obligera à implémenter cette méthode.

Bill le lézard
la source
1
Je pense que le compilateur devrait toujours lancer des avertissements concernant les classes abstraites qui implémentent des interfaces de manière incomplète, simplement parce que vous devez alors parcourir 2 définitions de classe plutôt que 1 pour voir ce dont vous avez besoin dans une sous-classe. Il s'agit cependant d'une limitation du langage / du compilateur.
workmad3
3
Ce ne serait pas une bonne idée, car il peut généralement y avoir beaucoup de classes abstraites et les «faux» avertissements vous submergeraient rapidement, vous faisant manquer les «vrais» avertissements. Si vous y réfléchissez, le mot-clé 'abstract' est là spécifiquement pour dire au compilateur de supprimer les avertissements pour cette classe.
belugabob
4
@workmad - si vous avez des implémentations communes pour un sous-ensemble de méthodes d'interface, il est préférable de le factoriser dans une classe de base distincte (DRY l'emporte sur le code à un seul endroit)
Gishu
4
Il serait dangereux de vous obliger à mettre des implémentations de méthodes vides dans une classe abstraite. Si vous faisiez cela, les implémenteurs de sous-classes hériteraient de ce non-comportement sans que le compilateur ne leur dise qu'il y a un problème.
Bill the Lizard
8
Je pense que ce que Workmad pourrait suggérer, c'est que vous définissiez les méthodes dans la classe abstraite sans corps de méthode et que vous les marquiez comme abstraites. Cela ne me semble pas une mauvaise idée.
Dónal
33

Parfaitement bien.
Vous ne pouvez pas instancier des classes abstraites ... mais des classes abstraites peuvent être utilisées pour héberger des implémentations courantes pour m1 () et m3 ().
Donc, si l' implémentation de m2 () est différente pour chaque implémentation mais que m1 et m3 ne le sont pas. Vous pouvez créer différentes implémentations concrètes de IAnything avec juste l'implémentation différente de m2 et dériver de AbstractThing - en respectant le principe DRY. Valider si l'interface est complètement implémentée pour une classe abstraite est futile.

Mise à jour : Fait intéressant, je trouve que C # applique cela comme une erreur de compilation. Vous êtes obligé de copier les signatures de méthode et de les préfixer avec 'abstract public' dans la classe de base abstraite dans ce scénario. (Quelque chose de nouveau tous les jours :)

Gishu
la source
7

C'est très bien. Pour comprendre ce qui précède, vous devez d'abord comprendre la nature des classes abstraites. Ils sont similaires aux interfaces à cet égard. C'est ce que dit Oracle à ce sujet ici .

Les classes abstraites sont similaires aux interfaces. Vous ne pouvez pas les instancier et ils peuvent contenir un mélange de méthodes déclarées avec ou sans implémentation.

Il faut donc réfléchir à ce qui se passe lorsqu'une interface étend une autre interface. Par exemple ...

//Filename: Sports.java
public interface Sports
{
   public void setHomeTeam(String name);
   public void setVisitingTeam(String name);
}

//Filename: Football.java
public interface Football extends Sports
{
   public void homeTeamScored(int points);
   public void visitingTeamScored(int points);
   public void endOfQuarter(int quarter);
}

... comme vous pouvez le voir, cela compile également parfaitement bien. Tout simplement parce que, tout comme une classe abstraite, une interface ne peut PAS être instanciée. Ainsi, il n'est pas nécessaire de mentionner explicitement les méthodes de son "parent". Cependant, TOUTES les signatures de méthode parente font implicitement partie de l'interface d'extension ou d'implémentation de la classe abstraite. Ainsi, une fois qu'une classe appropriée (une qui peut être instanciée) étend ce qui précède, il sera nécessaire de s'assurer que chaque méthode abstraite est implémentée.

J'espère que cela aide ... et Allahu 'alam!

Reconnaissant
la source
C'est un point de vue intéressant. Cela me fait penser que les "classes abstraites" sont en réalité des "interfaces concrètes", c'est-à-dire des interfaces avec des méthodes concrètes, plutôt que des classes avec des méthodes abstraites.
Giulio Piancastelli
... un peu des deux vraiment. Mais une chose est sûre, ils ne sont pas instanciables.
Reconnaissant
4

Interface signifie une classe qui n'a pas d'implémentation de sa méthode, mais avec juste une déclaration.
D'autre part, la classe abstraite est une classe qui peut avoir une implémentation d'une méthode avec une méthode avec juste déclaration, sans implémentation.
Lorsque nous implémentons une interface dans une classe abstraite, cela signifie que la classe abstraite a hérité de toutes les méthodes de l'interface. Comme, il n'est pas important d'implémenter toute la méthode dans une classe abstraite, mais il s'agit d'une classe abstraite (par héritage aussi), donc la classe abstraite peut laisser une partie de la méthode dans l'interface sans implémentation ici. Mais, lorsque cette classe abstraite sera héritée par une classe concrète, ils doivent avoir à implémenter toutes ces méthodes non implémentées dans une classe abstraite.

Mustakimur Rahman
la source
4

Compte tenu de l'interface:

public interface IAnything {
  int i;
  void m1();
  void m2();
  void m3();
}

Voici comment Java le voit:

public interface IAnything {
  public static final int i;
  public abstract void m1();
  public abstract void m2();
  public abstract void m3();
}

Vous pouvez donc laisser certaines (ou toutes) de ces abstractméthodes non implémentées, comme vous le feriez dans le cas de abstractclasses étendant une autreabstract classe.

Lorsque vous implementun interface, la règle selon laquelle toutes les interfaceméthodes doivent être implémentées dans le dérivé classs'applique uniquement à l' classimplémentation concrète (c'est-à-dire qui n'est pasabstract elle-même).

Si vous envisagez en effet de créer un abstract classhors de celui-ci, alors il n'y a pas de règle qui dit que vous avez implementtoutes les interfaceméthodes (notez que dans un tel cas, il est obligatoire de déclarer le dérivé classcomme abstract)

sharhp
la source
Utilisation javap IAnything.classpour générer le deuxième extrait de code.
sharhp
3

Quand une classe abstraite implémente une interface

Dans la section sur les interfaces, il a été noté qu'une classe qui implémente une interface doit implémenter toutes les méthodes de l'interface. Il est cependant possible de définir une classe qui n'implémente pas toutes les méthodes de l'interface, à condition que la classe soit déclarée abstraite. Par exemple,

abstract class X implements Y {   
    // implements all but one method of Y
}

class XX extends X {   
    // implements the remaining method in Y 
} 

Dans ce cas, la classe X doit être abstraite car elle n'implémente pas complètement Y, mais la classe XX implémente en fait Y.

Référence: http://docs.oracle.com/javase/tutorial/java/IandI/abstract.html

Do Nhu Vy
la source
1

Les classes abstraites ne sont pas nécessaires pour implémenter les méthodes. Ainsi, même si elle implémente une interface, les méthodes abstraites de l'interface peuvent rester abstraites. Si vous essayez d'implémenter une interface dans une classe concrète (c'est-à-dire pas abstraite) et que vous n'implémentez pas les méthodes abstraites, le compilateur vous dira: Soit implémentez les méthodes abstraites, soit déclarez la classe comme abstraite.

Vincent Ramdhanie
la source