L'une des fonctionnalités les plus utiles de Java 8 est la nouvelle default
méthodes sur les interfaces. Il y a essentiellement deux raisons (il peut y en avoir d'autres) pour lesquelles elles ont été introduites:
- Fournir des implémentations par défaut réelles. Exemple:
Iterator.remove()
- Permettant l'évolution de l'API JDK. Exemple:
Iterable.forEach()
Du point de vue d'un concepteur d'API, j'aurais aimé pouvoir utiliser d'autres modificateurs sur les méthodes d'interface, par exemple final
. Cela serait utile lors de l'ajout de méthodes pratiques, empêchant les remplacements "accidentels" dans l'implémentation des classes:
interface Sender {
// Convenience method to send an empty message
default final void send() {
send(null);
}
// Implementations should only implement this method
void send(String message);
}
Ce qui précède est déjà une pratique courante si Sender
était une classe:
abstract class Sender {
// Convenience method to send an empty message
final void send() {
send(null);
}
// Implementations should only implement this method
abstract void send(String message);
}
Maintenant, default
et final
sont évidemment des mots-clés contradictoires, mais le mot-clé par défaut lui-même n'aurait pas été strictement requis , donc je suppose que cette contradiction est délibérée, pour refléter les différences subtiles entre les "méthodes de classe avec le corps" (juste méthodes) et "interface" méthodes avec corps " (méthodes par défaut), c'est-à-dire des différences que je n'ai pas encore comprises.
À un certain moment, la prise en charge des modificateurs comme static
et final
sur les méthodes d'interface n'était pas encore complètement explorée, citant Brian Goetz :
L'autre partie est de savoir jusqu'où nous allons aller pour prendre en charge les outils de création de classe dans les interfaces, telles que les méthodes finales, les méthodes privées, les méthodes protégées, les méthodes statiques, etc. La réponse est: nous ne savons pas encore
Depuis cette époque fin 2011, évidemment, le support des static
méthodes dans les interfaces a été ajouté. De toute évidence, cela a ajouté beaucoup de valeur aux bibliothèques JDK elles-mêmes, comme avec Comparator.comparing()
.
Question:
Quelle est la raison final
(et aussi static final
) qui n'est jamais arrivée aux interfaces Java 8?
la source
final
empêche une méthode d'être surchargée, et vu comment vous DEVEZ remplacer les méthodes héritées des interfaces, je ne vois pas pourquoi il serait logique de la rendre définitive. A moins que cela ne signifie que la méthode est définitive APRÈS l'avoir remplacée une fois .. Dans ce cas, peut-être y a-t-il des difficultés? Si je ne comprends pas ce droit, laissez-moi kmow. Semble intéressantfinal
aurait une utilité pour empêcher l'implémentation des classes de remplacer l'implémentation par défaut d'une méthode d'interface.Réponses:
Cette question est, dans une certaine mesure, liée à Quelle est la raison pour laquelle «synchronisé» n'est pas autorisé dans les méthodes d'interface Java 8?
L'essentiel à comprendre au sujet des méthodes par défaut est que l'objectif principal de la conception est l' évolution de l'interface , et non "transformer les interfaces en traits (médiocres)". Bien qu'il y ait un certain chevauchement entre les deux, et nous avons essayé de nous adapter à ce dernier là où il ne gênait pas le premier, ces questions sont mieux comprises lorsqu'elles sont considérées sous cet angle. (Notez également que les méthodes de classe sont vont être différentes des méthodes d'interface, peu importe ce que l'intention, en vertu du fait que l' on peut multiplier les méthodes héritées de l' interface.)
L'idée de base d'une méthode par défaut est: c'est une méthode d'interface avec une implémentation par défaut, et une classe dérivée peut fournir une implémentation plus spécifique. Et parce que le centre de conception était l'évolution des interfaces, c'était un objectif de conception critique que les méthodes par défaut puissent être ajoutées aux interfaces après coup. une manière compatible avec les sources et compatible binaire.
La réponse trop simple à "pourquoi pas les méthodes par défaut finales" est qu'alors le corps ne serait plus simplement l'implémentation par défaut, ce serait la seule implémentation. Bien que ce soit une réponse un peu trop simple, cela nous donne un indice que la question va déjà dans une direction discutable.
Une autre raison pour laquelle les méthodes d'interface finales sont discutables est qu'elles créent des problèmes impossibles pour les implémenteurs. Par exemple, supposons que vous ayez:
Ici, tout va bien;
C
héritefoo()
deA
. Supposons maintenant que l'B
on change pour avoir unefoo
méthode, avec une valeur par défaut:Maintenant, lorsque nous allons recompiler
C
, le compilateur nous dira qu'il ne sait pas quel comportement hériterfoo()
, ilC
doit donc le remplacer (et pourrait choisir de déléguerA.super.foo()
s'il souhaite conserver le même comportement.) Mais que faire siB
avait fait son défautfinal
, etA
n'est pas sous le contrôle de l'auteur deC
? MaintenantC
est irrémédiablement brisé; il ne peut pas compiler sansfoo()
remplacer, mais il ne peut pas remplacerfoo()
s'il était final enB
.Ce n'est qu'un exemple, mais le fait est que la finalité des méthodes est vraiment un outil qui a plus de sens dans le monde des classes à héritage unique (généralement qui associent l'état au comportement), qu'aux interfaces qui contribuent simplement au comportement et peuvent être multipliées hérité. Il est trop difficile de raisonner sur "quelles autres interfaces pourraient être mélangées dans l'implémentateur éventuel", et permettre à une méthode d'interface d'être finale causerait probablement ces problèmes (et ils exploseraient non pas sur la personne qui a écrit l'interface, mais sur le utilisateur pauvre qui essaie de l'implémenter.)
Une autre raison de les interdire est qu'ils ne signifieraient pas ce que vous pensez qu'ils signifient. Une implémentation par défaut n'est prise en compte que si la classe (ou ses superclasses) ne fournit pas de déclaration (concrète ou abstraite) de la méthode. Si une méthode par défaut était finale, mais qu'une superclasse avait déjà implémenté la méthode, la valeur par défaut serait ignorée, ce qui n'est probablement pas ce à quoi l'auteur par défaut s'attendait en la déclarant finale. (Ce comportement d'héritage reflète le centre de conception des méthodes par défaut - évolution de l'interface. Il devrait être possible d'ajouter une méthode par défaut (ou une implémentation par défaut à une méthode d'interface existante) aux interfaces existantes qui ont déjà des implémentations, sans modifier la comportement des classes existantes qui implémentent l'interface,
la source
La liste de diffusion lambda contient de nombreuses discussions à ce sujet . L'un de ceux qui semble contenir beaucoup de discussions sur tout cela est le suivant: Sur la visibilité de la méthode d'interface variée (était Final Defenders) .
Dans cette discussion, Talden, l'auteur de la question d'origine pose quelque chose de très similaire à votre question:
Finalement, Brian Goetz a répondu :
Il est donc très probable qu'il n'ait jamais été mis en œuvre car il ne faisait jamais partie de la portée. Il n'a jamais été proposé à temps pour être examiné.
Dans une autre discussion animée sur les méthodes du défenseur final sur le sujet, Brian a de nouveau déclaré :
Donc, cela renforce ma théorie selon laquelle cela ne faisait tout simplement pas partie de la portée ou de la conception. Ce qu'ils ont fait était de fournir suffisamment de fonctionnalités pour faire face aux problèmes de l'évolution des API.
la source
Il sera difficile de trouver et identifier « LA » réponse, pour les resons mentionnés dans les commentaires de @EJP: Il y a environ 2 (+/- 2) personnes dans le monde qui peut donner la réponse définitive du tout . Et dans le doute, la réponse pourrait simplement être quelque chose comme "La prise en charge des méthodes par défaut finales ne semble pas valoir la peine de restructurer les mécanismes internes de résolution des appels". C'est de la spéculation, bien sûr, mais elle est au moins étayée par des preuves subtiles, comme cette déclaration (par l'une des deux personnes) dans la liste de diffusion OpenJDK :
et des faits triviaux comme le fait qu'une méthode n'est tout simplement pas considérée comme une méthode (vraiment) finale lorsqu'elle est une
default
méthode, comme cela est actuellement implémenté dans la méthode Method :: is_final_method dans OpenJDK.Il est en effet difficile de trouver des informations vraiment "autoritaires", même avec des recherches Web excessives et en lisant les journaux de validation. J'ai pensé que cela pourrait être lié à des ambiguïtés potentielles lors de la résolution des appels de méthode d'interface avec l'
invokeinterface
instruction et et les appels de méthode de classe, correspondant à l'invokevirtual
instruction: Pour l'invokevirtual
instruction, il peut y avoir une simple recherche de table , car la méthode doit soit être héritée à partir d'une superclasse, ou implémenté directement par la classe. Contrairement à cela, uninvokeinterface
appel doit examiner le site d'appel respectif pour savoir à quelle interface cet appel se réfère réellement (cela est expliqué plus en détail dans les InterfaceCalls page du HotSpot Wiki). cependant,final
les méthodes ne sont pas du tout insérées dans la table virtuelle ou remplacent les entrées existantes dans la table virtuelle (voir klassVtable.cpp. Ligne 333 ), et de même, les méthodes par défaut remplacent les entrées existantes dans la table virtuelle (voir klassVtable.cpp, ligne 202 ) . La raison réelle (et donc la réponse) doit donc être cachée plus profondément dans les mécanismes (plutôt complexes) de résolution d'appels de la méthode, mais peut-être que ces références seront néanmoins considérées comme utiles, ne serait-ce que pour d'autres qui parviennent à dériver la réponse réelle à partir de ce.la source
int
s non signé , mais voir stackoverflow.com/questions/430346 ... ...synchronized
lesdefault
méthodes.final
question, et il est quelque peu humiliant que personne d'autre ne semble penser à des exemples similaires (ou, peut-être, certains ont pensé à ces exemples, mais ne se sont pas sentis suffisamment autoritaires pour répondre). Alors maintenant (désolé, je dois le faire:) le dernier mot est prononcé.Je ne pense pas qu'il soit nécessaire de spécifier
final
une méthode d'interface de convenance, je suis d'accord pour dire que cela peut être utile, mais apparemment les coûts ont surpassé les avantages.Ce que vous êtes censé faire, de toute façon, est d'écrire du javadoc approprié pour la méthode par défaut, en montrant exactement ce que la méthode est et n'est pas autorisée à faire. De cette façon, les classes implémentant l'interface "ne sont pas autorisées" à modifier l'implémentation, bien qu'il n'y ait aucune garantie.
N'importe qui pourrait écrire un
Collection
qui adhère à l'interface et ensuite faire des choses dans les méthodes qui sont absolument contre-intuitives, il n'y a aucun moyen de s'en protéger, à part écrire des tests unitaires approfondis.la source
final
a été décidé de ne pas être autorisé sur lesinterface
méthodes Java 8 . Un rapport coût / bénéfice insuffisant est un bon candidat, mais jusqu'à présent, c'est de la spéculation.Nous ajoutons un
default
mot-clé à notre méthode à l'intérieur d'uninterface
quand nous savons que la classe étendant la mise en œuvreinterface
peut ou nonoverride
notre implémentation. Mais que se passe-t-il si nous voulons ajouter une méthode que nous ne voulons pas qu'une classe d'implémentation écrase? Eh bien, deux options s'offraient à nous:default
final
méthode.static
méthode.Maintenant, Java dit que si nous en
class
implémentons deux ou plusinterfaces
, ils ont unedefault
méthode avec exactement le même nom de méthode et la même signature, c'est-à-dire qu'ils sont en double, alors nous devons fournir une implémentation de cette méthode dans notre classe. Maintenant, dans le cas dedefault
final
méthodes, nous ne pouvons pas fournir une implémentation et nous sommes bloqués. Et c'est pourquoi lefinal
mot-clé n'est pas utilisé dans les interfaces.la source