Les méthodes par défaut sont un nouvel outil intéressant dans notre boîte à outils Java. Cependant, j'ai essayé d'écrire une interface qui définit une default
version de la toString
méthode. Java me dit que cela est interdit, car les méthodes déclarées dans java.lang.Object
ne peuvent pas être default
éditées. pourquoi est-ce le cas?
Je sais qu'il existe la règle "la classe de base gagne toujours", donc par défaut (jeu de mots;), toute default
implémentation d'une Object
méthode serait écrasée par la méthode de Object
toute façon. Cependant, je ne vois aucune raison pour laquelle il ne devrait pas y avoir d'exception pour les méthodes de Object
la spécification. Surtout toString
qu'il peut être très utile d'avoir une implémentation par défaut.
Alors, quelle est la raison pour laquelle les concepteurs Java ont décidé de ne pas autoriser les default
méthodes remplaçant les méthodes Object
?
la source
Réponses:
C'est encore un autre de ces problèmes de conception de langage qui semble "évidemment une bonne idée" jusqu'à ce que vous commenciez à creuser et que vous vous rendiez compte que c'est en fait une mauvaise idée.
Ce courrier a beaucoup sur le sujet (et sur d'autres sujets aussi.) Il y avait plusieurs forces de conception qui ont convergé pour nous amener à la conception actuelle:
AbstractList
une interface), vous vous rendez compte que l'héritage de equals / hashCode / toString est fortement lié à l'héritage et à l'état uniques, et les interfaces sont héritées de manière multiple et sans état;Vous avez déjà abordé l'objectif «garder les choses simples»; les règles d'héritage et de résolution des conflits sont conçues pour être très simples (les classes gagnent les interfaces, les interfaces dérivées l'emportent sur les super-interfaces, et tout autre conflit est résolu par la classe d'implémentation.) Bien sûr, ces règles pourraient être modifiées pour faire une exception, mais Je pense que vous constaterez lorsque vous commencez à tirer sur cette chaîne, que la complexité incrémentielle n'est pas aussi petite que vous pourriez le penser.
Bien sûr, il y a un certain degré d'avantages qui justifierait plus de complexité, mais dans ce cas, ce n'est pas là. Les méthodes dont nous parlons ici sont equals, hashCode et toString. Ces méthodes concernent toutes intrinsèquement l'état de l'objet, et c'est la classe qui possède l'état, et non l'interface, qui est la mieux placée pour déterminer ce que signifie l'égalité pour cette classe (d'autant plus que le contrat d'égalité est assez fort; voir Efficace Java pour des conséquences surprenantes); les écrivains d'interface sont trop éloignés.
Il est facile de sortir l'
AbstractList
exemple; ce serait bien si nous pouvions nous débarrasserAbstractList
et mettre le comportement dans l'List
interface. Mais une fois que vous avez dépassé cet exemple évident, il n'y a pas beaucoup d'autres bons exemples à trouver. À la racine,AbstractList
est conçu pour l'héritage unique. Mais les interfaces doivent être conçues pour l'héritage multiple.De plus, imaginez que vous écrivez cette classe:
L'
Foo
auteur examine les supertypes, ne voit aucune implémentation d'égaux et conclut que pour obtenir l'égalité de référence, il lui suffit d'hériter des égauxObject
. Puis, la semaine prochaine, le responsable de la bibliothèque de Bar ajoute "utilement" uneequals
implémentation par défaut . Oups! Maintenant, la sémantique deFoo
a été brisée par une interface dans un autre domaine de maintenance, ajoutant "utilement" une valeur par défaut pour une méthode commune.Les valeurs par défaut sont censées être des valeurs par défaut. L'ajout d'une valeur par défaut à une interface où il n'y en avait pas (n'importe où dans la hiérarchie) ne devrait pas affecter la sémantique des classes d'implémentation concrètes. Mais si les valeurs par défaut pouvaient "remplacer" les méthodes Object, ce ne serait pas vrai.
Ainsi, bien que cela semble être une fonctionnalité inoffensive, elle est en fait assez nuisible: elle ajoute beaucoup de complexité pour peu d'expressivité incrémentale, et cela rend beaucoup trop facile pour des modifications bien intentionnées et inoffensives des interfaces compilées séparément de saper la sémantique prévue de l'implémentation des classes.
la source
hashCode
etequals
, mais je pense que ce serait très utile pourtoString
. Par exemple, uneDisplayable
interface pourrait définir uneString display()
méthode, et cela économiserait une tonne de passe-partout pour pouvoir définirdefault String toString() { return display(); }
dansDisplayable
, au lieu d'exiger que chacunDisplayable
implémentetoString()
ou étende uneDisplayableToString
classe de base.toString()
est basé uniquement sur les méthodes d'interface, vous pouvez simplement ajouter quelque chose commedefault String toStringImpl()
à l'interface, et remplacertoString()
dans chaque sous-classe pour appeler l'implémentation de l'interface - un peu moche, mais fonctionne, et mieux que rien. :) Une autre façon de le faire est de créer quelque chose commeObjects.hash()
,Arrays.deepEquals()
etArrays.deepToString()
. +1 pour la réponse de @ BrianGoetz!default toString()
remplacement dans une interface fonctionnelle nous permettrait - au moins - de faire quelque chose comme cracher la signature de la fonction et la classe parente de l'implémenteur. Mieux encore, si nous pouvions appliquer des stratégies toString récursives, nous pourrions parcourir la fermeture pour obtenir une très bonne description du lambda, et ainsi améliorer considérablement la courbe d'apprentissage lambda.Il est interdit de définir des méthodes par défaut dans les interfaces pour les méthodes de
java.lang.Object
, car les méthodes par défaut ne seraient jamais "accessibles".Les méthodes d'interface par défaut peuvent être écrasées dans les classes implémentant l'interface et l'implémentation de classe de la méthode a une priorité plus élevée que l'implémentation d'interface, même si la méthode est implémentée dans une superclasse. Étant donné que toutes les classes héritent de
java.lang.Object
, les méthodes dansjava.lang.Object
auraient la priorité sur la méthode par défaut dans l'interface et seraient appelées à la place.Brian Goetz d'Oracle fournit quelques détails supplémentaires sur la décision de conception dans cet article de liste de diffusion .
la source
Je ne vois pas dans la tête des auteurs du langage Java, donc on ne peut que deviner. Mais je vois de nombreuses raisons et je suis tout à fait d'accord avec elles dans ce numéro.
La principale raison de l'introduction des méthodes par défaut est de pouvoir ajouter de nouvelles méthodes aux interfaces sans rompre la compatibilité descendante des anciennes implémentations. Les méthodes par défaut peuvent également être utilisées pour fournir des méthodes «pratiques» sans qu'il soit nécessaire de les définir dans chacune des classes d'implémentation.
Aucun de ces éléments ne s'applique à toString et aux autres méthodes d'Object. En termes simples, les méthodes par défaut ont été conçues pour fournir le comportement par défaut là où il n'y a pas d'autre définition. Ne pas fournir des implémentations qui "concurrenceront" d'autres implémentations existantes.
La règle «la classe de base gagne toujours» a aussi ses bonnes raisons. On suppose que les classes définissent des implémentations réelles , tandis que les interfaces définissent des implémentations par défaut , qui sont un peu plus faibles.
En outre, l'introduction de TOUTES exceptions aux règles générales entraîne une complexité inutile et soulève d'autres questions. L'objet est (plus ou moins) une classe comme une autre, alors pourquoi devrait-il avoir un comportement différent?
Dans l'ensemble, la solution que vous proposez apporterait probablement plus d'inconvénients que d'avantages.
la source
Le raisonnement est très simple, c'est parce que Object est la classe de base de toutes les classes Java. Donc, même si la méthode Object est définie comme méthode par défaut dans certaines interfaces, elle sera inutile car la méthode Object sera toujours utilisée. C'est pourquoi, pour éviter toute confusion, nous ne pouvons pas avoir de méthodes par défaut qui remplacent les méthodes de classe Object.
la source
Pour donner une réponse très pédante, il est uniquement interdit de définir une
default
méthode pour une méthode publique à partir dejava.lang.Object
. Il existe 11 méthodes à considérer, qui peuvent être classées de trois façons pour répondre à cette question.Object
méthodes ne peut pas avoir desdefault
méthodes parce qu'ils sontfinal
et ne peuvent être écartés du tout:getClass()
,notify()
,notifyAll()
,wait()
,wait(long)
etwait(long, int)
.Object
méthodes ne peut pas avoir desdefault
méthodes pour les raisons indiquées ci - dessus par Brian Goetz:equals(Object)
,hashCode()
ettoString()
.Deux des
Object
méthodes peuvent avoir desdefault
méthodes, bien que la valeur de ces valeurs par défaut soit au mieux discutable:clone()
etfinalize()
.la source