Qui étend les interfaces? Et pourquoi?

20

AFAIK, mes extendsclasses et implementsinterfaces parentales . Mais je rencontre une situation où je ne peux pas l'utiliser implements SomeInterface. C'est la déclaration d'un type générique. Par exemple:

public interface CallsForGrow {...}

public class GrowingArrayList <T implements CallsForGrow>  // BAD, won't work!
                              extends ArrayList<T> 

Ici, l'utilisation implementsest interdite syntaxiquement. J'ai d'abord pensé qu'il était interdit d'utiliser l'interface à l'intérieur de <>, mais non. C'est possible, je n'ai qu'à utiliser à la extendsplace de implements. En conséquence, je "prolonge" une interface. Cet autre exemple fonctionne:

public interface CallsForGrow {...}

public class GrowingArrayList <T extends CallsForGrow>  // this works!
                              extends ArrayList<T> 

Cela me semble être une incohérence syntaxique. Mais peut-être que je ne comprends pas certaines finesses de Java 6? Y a-t-il d'autres endroits où je devrais étendre les interfaces? L'interface, que je veux étendre, devrait-elle avoir des fonctionnalités spéciales?

Gangnus
la source

Réponses:

25

Dans le cas des variables de type génériques, le compilateur ne se soucie pas réellement s'il Ts'agit d'une classe, d'une interface, d'une énumération ou d'une annotation. Tout ce qui compte c'est qu'il s'agit d'un type avec un ensemble donné de sous-et super-types.

Et il n'y a aucune raison de compliquer la syntaxe simplement parce qu'à un autre endroit (où vous implémentez réellement une interface) la distinction entre les classes et les interfaces est pertinente (par exemple, si vous êtes implementune interface, vous devez implémenter toutes les méthodes qu'elle définit, mais vous pas besoin, si vous êtes extendune classe (non abstraite)).

En supposant un instant que vous deviez écrire implementsici, vous auriez également besoin d'une syntaxe distincte pour les enumvaleurs (au lieu de simplement écrire <E extends Enum<E>>) et les annotations (que vous pouvez facilement déclarer en utilisant <A extends Annotation>).

Le seul endroit où vous devez écrire implementsest au point où vous implémentez réellement l'interface. À ce stade (et à ce stade uniquement), la différence est importante, car vous devez implémenter les méthodes définies dans l'interface. Pour tout le monde, peu importe si une donnée Aest une classe de base ou une interface implémentée de B: c'est un super-type et c'est tout ce qui compte.

Notez également qu'il existe un autre endroit où vous utilisez extendsdes interfaces:

interface A {
}

interface B extends A {
}

À ce stade, ce implementsserait faux, car B ne met pas en œuvre A .

Joachim Sauer
la source
Si nous utiliserons vos logiques, nous devrions utiliser « étend UneInterface » toujours , non seulement dans les génériques.
Gangnus
2
@Gangnus: non, car pour l' implémenteur il y a une différence concrète entre extendset implements, c'est juste quand on regarde le problème du point de vue du système de type pur, qu'il n'y a pas de différence.
Joachim Sauer
1
Désolé, je ne comprends pas votre pensée. --1. Pourquoi devrions-nous utiliser "une perspective de type système pur" dans un cas et pas dans l'autre? --2. Pourquoi les exigences syntaxiques changent dans ces différents endroits? Pourriez-vous l'expliquer, s'il vous plaît. Dans la réponse, si possible.
Gangnus
1
@Gangnus: qui a dit que c'était Tune classe? Tpourrait référencer un type d'interface ou un enumtype.
Joachim Sauer
1
venant de C #, toute cette discussion est ridicule. nous désignons les supertypes d'un type avec :. et sous les couvertures, une interface est et a toujours été une classe abstraite avec la contrainte de ne contenir que des abstractmembres virtual ( ) non implémentés , afin de permettre l'héritage multiple. il n'y a littéralement aucune différence entre implementset extends. ils pourraient tous deux être remplacés par le même mot ( extends) ou par un symbole arbitraire ( :) et rien ne serait perdu.
sara
5

Lorsque Java 5, et en particulier les génériques, a été initialement mis à la disposition des développeurs qui avaient manifesté un intérêt, la syntaxe était tout à fait différente. Au lieu de Set<? extends Foo>et Set<? super Bar>il y avait Set<+Foo>et Set<-Foo>. Cependant, la rétroaction était qu'il n'était pas clair si cela +signifiait plus spécifique ou plus large (plus de classes) . Sun a répondu à cette rétroaction en changeant la syntaxe, mais dans la contrainte de ne pas introduire de nouveaux mots clés, ce qui aurait posé un problème de compatibilité descendante.

Le résultat est que ni l'un ni l'autre n'est tout à fait naturel. Comme vous le constatez, extendsest surchargé pour parler de classes "étendant" les interfaces, ce qui n'est pas le langage utilisé dans d'autres contextes; et superest surchargé pour signifier "est une superclasse de", qui est la direction opposée de la relation précédemment exprimée par le mot-clé, c'est-à-dire se référant à une superclasse.

Cependant, les principes fondamentaux de ce qu'est une interface ne sont pas affectés par ce changement, et il n'introduit pas d'interfaces spéciales qui sont étendues d'une nouvelle manière.

Peter Taylor
la source
+1. Je vois et je suis d'accord. Mais Joachim Sauer a trouvé ma pensée erronée que T est nécessairement une classe. Donc, la réponse est la sienne. Votre compréhension est d'un (ou 2) étage plus haut :-). Mon problème était plus primitif. Merci quand même pour mon développement.
Gangnus
2

Il y a des inconvénients à autoriser et à interdire une telle syntaxe, et ceux d' autoriser sont bien plus importants.

Pensez-y.

La séparation de l'interface et de l'implémentation est l'un des idiomes de programmation fondamentaux. Pour cette raison, la syntaxe permettant d'épeler "l'interface implémente quelque chose" serait à peu près aussi mauvaise que celle utilisant le signe plus pour désigner la multiplication des opérandes.

moucheron
la source
D'accord. Donc, je ne comprends vraiment pas quelque chose. Je l'ai considéré comme très probable. Mais vous n'avez pas du tout expliqué le problème, désolé. T est une représentation d'une classe. Pourquoi dans un endroit où j'utilise T étend et dans un autre T implémente?
Gangnus
@Gangnus dans l'exemple que vous avez fourni T fait référence à un paramètre de type qui n'est pas nécessairement une classe - voyez ce que Joachim vous a déjà dit . Permettre des outils pour cela conduirait au même gâchis que j'ai mentionné
moucher
Oui, j'ai déjà vu son commentaire. Il sera marqué comme la réponse une fois mis en réponse. Jusqu'à ce que vous ayez tous les deux mes remerciements et +1.
Gangnus
-1

Il faut comprendre la programmation pilotée par interface comme prochaine étape tout en comprenant une interface. Il indique quelle est l'utilisation réelle de l'interface. Quel rôle il joue dans un programme Java (ou tout autre langage).

Deepak
la source
2
Comment votre réponse répond-elle aux préoccupations du PO? Veuillez modifier votre réponse pour être plus clair.