Java 8 autorise les méthodes d'interface statique
Avec Java 8, les interfaces peuvent avoir des méthodes statiques. Ils peuvent également avoir des méthodes d'instance concrètes, mais pas des champs d'instance.
Il y a vraiment deux questions ici:
- Pourquoi, dans le mauvais vieux temps, les interfaces ne pouvaient-elles pas contenir de méthodes statiques?
- Pourquoi les méthodes statiques ne peuvent-elles pas être remplacées?
Méthodes statiques dans les interfaces
Il n'y avait aucune raison technique solide pour laquelle les interfaces n'auraient pas pu avoir de méthodes statiques dans les versions précédentes. Ceci est bien résumé par l'affiche d'une question en double. Les méthodes d'interface statique étaient initialement considérées comme un petit changement de langage, puis il y a eu une proposition officielle de les ajouter dans Java 7, mais elles ont été abandonnées par la suite en raison de complications imprévues.
Enfin, Java 8 a introduit des méthodes d'interface statique, ainsi que des méthodes d'instance remplaçables avec une implémentation par défaut. Cependant, ils ne peuvent toujours pas avoir de champs d'instance. Ces fonctionnalités font partie de la prise en charge de l'expression lambda, et vous pouvez en savoir plus à leur sujet dans la partie H de JSR 335.
Remplacement des méthodes statiques
La réponse à la deuxième question est un peu plus compliquée.
Les méthodes statiques peuvent être résolues au moment de la compilation. La répartition dynamique est logique pour les méthodes d'instance, où le compilateur ne peut pas déterminer le type concret de l'objet et, par conséquent, ne peut pas résoudre la méthode à appeler. Mais invoquer une méthode statique nécessite une classe, et puisque cette classe est connue statiquement - au moment de la compilation - la répartition dynamique n'est pas nécessaire.
Un petit historique sur le fonctionnement des méthodes d'instance est nécessaire pour comprendre ce qui se passe ici. Je suis sûr que l'implémentation réelle est assez différente, mais permettez-moi d'expliquer ma notion de répartition des méthodes, qui modélise avec précision le comportement observé.
Imaginez que chaque classe possède une table de hachage qui mappe les signatures de méthode (noms et types de paramètres) à un morceau de code réel pour implémenter la méthode. Lorsque la machine virtuelle tente d'appeler une méthode sur une instance, elle interroge l'objet pour sa classe et recherche la signature demandée dans la table de la classe. Si un corps de méthode est trouvé, il est appelé. Sinon, la classe parente de la classe est obtenue et la recherche y est répétée. Cela continue jusqu'à ce que la méthode soit trouvée, ou qu'il n'y ait plus de classes parentes - ce qui entraîne unNoSuchMethodError
.
Si une superclasse et une sous-classe ont toutes deux une entrée dans leurs tables pour la même signature de méthode, la version de la sous-classe est rencontrée en premier et la version de la superclasse n'est jamais utilisée - il s'agit d'une "substitution".
Supposons maintenant que nous ignorions l'instance d'objet et commençons simplement par une sous-classe. La résolution pourrait continuer comme ci-dessus, vous donnant une sorte de méthode statique "surchargeable". La résolution peut cependant se produire au moment de la compilation, car le compilateur démarre à partir d'une classe connue, plutôt que d'attendre jusqu'à l'exécution pour interroger un objet d'un type non spécifié pour sa classe. Il ne sert à rien de "remplacer" une méthode statique car on peut toujours spécifier la classe qui contient la version souhaitée.
"Interfaces" constructeur
Voici un peu plus de matériel pour répondre à la récente modification de la question.
Il semble que vous souhaitiez mandater efficacement une méthode de type constructeur pour chaque implémentation de IXMLizable
. Oubliez d'essayer d'appliquer cela avec une interface pendant une minute et prétendez que vous avez des classes qui répondent à cette exigence. Comment l'utiliseriez-vous?
class Foo implements IXMLizable<Foo> {
public static Foo newInstanceFromXML(Element e) { ... }
}
Foo obj = Foo.newInstanceFromXML(e);
Comme vous devez nommer explicitement le type concret Foo
lors de la "construction" du nouvel objet, le compilateur peut vérifier qu'il possède bien la méthode d'usine nécessaire. Et si ce n'est pas le cas, alors quoi? Si je peux mettre en œuvre un IXMLizable
qui n'a pas le « constructeur », et je créer une instance et le transmettre à votre code, il est unIXMLizable
avec toute l'interface nécessaire.
La construction fait partie de l'implémentation, pas de l'interface. Tout code qui fonctionne correctement avec l'interface ne se soucie pas du constructeur. Tout code qui se soucie du constructeur doit de toute façon connaître le type concret, et l'interface peut être ignorée.