Pourquoi pas de méthodes statiques dans les interfaces, mais des champs statiques et des classes internes corrects? [pré-Java 8] [dupliquer]

StackOverflow https://stackoverflow.com/questions/129267

  •  02-07-2019
  •  | 
  •  

Question

    

Cette question a déjà une réponse ici:

         

Quelques questions ont été posées ici sur les raisons pour lesquelles vous ne pouvez pas définir de méthodes statiques dans les interfaces, mais aucune d’entre elles ne résout une incohérence fondamentale: pourquoi pouvez-vous définir des champs statiques et des types internes statiques dans une interface, mais pas des méthodes statiques? ?

Les types internes statiques ne sont peut-être pas une comparaison juste, puisqu'il ne s'agit que du sucre syntaxique qui génère une nouvelle classe, mais pourquoi des champs mais pas des méthodes?

Un argument contre les méthodes statiques dans les interfaces est qu’il casse la stratégie de résolution de table virtuelle utilisée par la machine virtuelle, mais ne devrait-il pas s’appliquer de la même manière aux champs statiques, c’est-à-dire que le compilateur peut simplement l’aligner?

La cohérence est ce que je souhaite, et Java devrait soit ne prendre en charge aucune statique d'aucune forme dans une interface, soit être cohérent et le permettre.

Était-ce utile?

La solution

Une proposition officielle a été faite pour autoriser les méthodes statiques dans les interfaces. en Java 7. Cette proposition est présentée sous la Pièce Coin .

Mon opinion personnelle est que c'est une excellente idée. Il n’ya pas de difficulté technique lors de la mise en oeuvre et c’est une chose très logique et raisonnable à faire. Il existe plusieurs propositions dans Project Coin qui, je l’espère, ne seront jamais intégrées au langage Java, mais c’est une proposition qui pourrait nettoyer beaucoup d’API. Par exemple, le La classe Collections a des méthodes statiques pour manipuler toute implémentation de List ; ceux-ci pourraient être inclus dans l'interface List .

Mise à jour: Dans le Podcast Java Posse # 234, Joe D'arcy a brièvement mentionné la proposition, affirmant qu'elle était "complexe". et ne le ferait probablement pas sous Project Coin.

Mise à jour: Bien qu'ils n'aient pas été inclus dans Project Coin pour Java 7, Java 8 prend en charge fonctions statiques dans les interfaces.

Autres conseils

Je vais suivre ma théorie des animaux de compagnie avec celle-ci, à savoir que le manque de cohérence dans ce cas est une question de commodité plutôt que de conception ou de nécessité, car je n'ai entendu aucun argument convaincant selon lequel c'était de ces deux.

Les champs statiques existent (a) car ils étaient présents dans JDK 1.0 et que de nombreuses décisions douteuses ont été prises dans JDK 1.0, et (b) les champs finaux statiques dans les interfaces sont ce qui se rapproche le plus des constantes java à l'époque.

Les classes internes statiques dans les interfaces étaient autorisées car il s'agissait d'un pur sucre syntaxique: la classe interne n'a rien à voir avec la classe parente.

Les méthodes statiques ne sont donc pas autorisées simplement parce qu'il n'y a aucune raison impérieuse de le faire. la cohérence n'est pas suffisamment convaincante pour changer le statu quo.

Bien sûr, cela pourrait être autorisé dans les futures versions de JLS sans rien casser.

Il n'y a jamais de raison de déclarer une méthode statique dans une interface. Ils ne peuvent pas être exécutés par l'appel normal MyInterface.staticMethod (). (EDIT: Depuis que cette dernière phrase a embrouillé certaines personnes, l'appel de MyClass.staticMethod () exécute précisément l'implémentation de staticMethod sur MyClass, qui, si MyClass est une interface ne peut pas exister!) Si vous les appelez en spécifiant la classe d'implémentation MyImplementor.staticMethod () alors vous devez connaître la classe réelle, il est donc indifférent que l'interface le contienne ou non.

Plus important encore, les méthodes statiques ne sont jamais remplacées, et si vous essayez de le faire:

MyInterface var = new MyImplementingClass();
var.staticMethod();

Les règles pour static indiquent que la méthode définie dans le type déclaré de var doit être exécutée. Puisque c'est une interface, c'est impossible.

Bien sûr, vous pouvez toujours supprimer le mot clé static de la méthode. Tout fonctionnera bien. Vous devrez peut-être supprimer certains avertissements si elle est appelée à partir d'une méthode d'instance.

Pour répondre à certains des commentaires ci-dessous, indiquez le motif de l'impossibilité d'exécuter "result = MyInterface.staticMethod ()". est qu'il faudrait qu'il exécute la version de la méthode définie dans MyInterface. Mais il ne peut y avoir de version définie dans MyInterface, car c'est une interface. Il n'a pas de code par définition.

Les interfaces ont pour objet de définir un contrat sans fournir d'implémentation. Par conséquent, vous ne pouvez pas avoir de méthodes statiques, car elles devraient déjà avoir une implémentation dans l'interface car vous ne pouvez pas écraser les méthodes statiques. En ce qui concerne les champs, seuls les champs finaux statiques sont autorisés. Il s'agit essentiellement de constantes (dans la version 1.5 ou plus, vous pouvez également avoir des énumérations dans les interfaces). Les constantes sont là pour aider à définir l’interface sans nombres magiques.

BTW, il n'est pas nécessaire de spécifier explicitement les modificateurs static final pour les champs des interfaces, car seuls les champs finaux statiques sont autorisés.

C’est un vieux fil, mais c’est une question très importante pour tous. Depuis que je l’ai remarqué aujourd’hui seulement, j’essaie donc de l’expliquer de manière plus nette:

Le but principal de l'interface est de fournir quelque chose d'inimplémentable, donc si elles fournissent

  

méthodes statiques à autoriser

alors vous pouvez appeler cette méthode en utilisant interfaceName.staticMethodName () , mais cette méthode n'est pas implémentée et ne contient rien. Il est donc inutile d'autoriser les méthodes statiques. Par conséquent, ils ne fournissent pas cela du tout.

  

les champs statiques sont autorisés

Parce que les champs ne sont pas implémentables, je veux dire par implémentation que vous ne pouvez effectuer aucune opération logique dans un champ, vous pouvez effectuer une opération sur un champ. Donc, vous ne changez pas le comportement des champs, c’est pourquoi ils sont autorisés.

  

Les classes intérieures sont autorisées

Les classes internes sont autorisées car après la compilation, différents fichiers de classe de la classe Inner sont créés, par exemple NomInterface $ InnerClassName.class . Vous fournissez donc une implémentation dans différentes entités, mais pas dans l'interface. Donc, la mise en oeuvre dans les classes intérieures est fournie.

J'espère que cela vous aidera.

En fait, il y a parfois des raisons pour lesquelles une personne peut bénéficier de méthodes statiques. Ils peuvent être utilisés comme méthodes d'usine pour les classes qui implémentent l'interface. Par exemple, c’est la raison pour laquelle nous avons maintenant l’interface Collection et la classe Collections dans openjdk. Il existe donc toujours des solutions de contournement: fournissez à une autre classe un constructeur privé qui servira de "lieu de nommage". pour les méthodes statiques.

Avant Java 5, les champs statiques étaient couramment utilisés:

interface HtmlConstants {
    static String OPEN = "<";
    static String SLASH_OPEN = "</";
    static String CLOSE = ">";
    static String SLASH_CLOSE = " />";
    static String HTML = "html";
    static String BODY = "body";
    ...
}

public class HtmlBuilder implements HtmlConstants { // implements ?!?
    public String buildHtml() {
       StringBuffer sb = new StringBuffer();
       sb.append(OPEN).append(HTML).append(CLOSE);
       sb.append(OPEN).append(BODY).append(CLOSE);
       ...
       sb.append(SLASH_OPEN).append(BODY).append(CLOSE);
       sb.append(SLASH_OPEN).append(HTML).append(CLOSE);
       return sb.toString();
    }
}

Cela signifiait que HtmlBuilder n'aurait pas à qualifier chaque constante, il pourrait donc utiliser OPEN au lieu de HtmlConstants.OPEN

.

L’utilisation d’outils de cette manière prête finalement à confusion.

Maintenant avec Java 5, nous avons la syntaxe importer statique pour obtenir le même effet:

private final class HtmlConstants {
    ...
    private HtmlConstants() { /* empty */ }
}

import static HtmlConstants.*;
public class HtmlBuilder { // no longer uses implements
    ...
}

Il n'y a pas de vraie raison pour ne pas avoir de méthodes statiques dans les interfaces, excepté: les concepteurs du langage Java ne le voulaient pas. D'un point de vue technique, il serait logique de les autoriser. Après tout, une classe abstraite peut les avoir aussi. Je suppose, mais je ne l'ai pas testé, que vous pouvez "créer de la main". code d'octet où l'interface a une méthode statique et devrait fonctionner sans problème pour appeler la méthode et / ou utiliser l'interface comme d'habitude.

Je me demande souvent pourquoi les méthodes statiques? Ils ont leur utilité, mais les méthodes de niveau package / namespace couvriraient probablement 80% des méthodes statiques utilisées.

Deux raisons principales me viennent à l’esprit:

  1. Les méthodes statiques en Java ne peuvent pas être remplacées par des sous-classes, ce qui est beaucoup plus complexe que les champs statiques. En pratique, je n'ai même jamais voulu remplacer un champ dans une sous-classe, mais je substitue tout le temps des méthodes. Donc, avoir des méthodes statiques empêche une classe implémentant l'interface de fournir sa propre implémentation de cette méthode, ce qui contrecarre en grande partie le but d'utiliser une interface.

  2. Les interfaces ne sont pas supposées avoir du code; c'est à ça que servent les classes abstraites. L’intérêt d’une interface est de vous permettre de parler d’objets non liés qui ont tous un certain ensemble de méthodes. En réalité, fournir une implémentation de ces méthodes est en dehors des limites de ce que les interfaces doivent être.

Les méthodes statiques sont liées à une classe. En Java, une interface n’est pas techniquement une classe, c’est un type, mais pas une classe (par conséquent, le mot clé implémente et les interfaces n’étendent pas Object). Les interfaces n'étant pas des classes, elles ne peuvent pas avoir de méthodes statiques, car il n'y a pas de classe à attacher.

Vous pouvez appeler InterfaceName.class pour obtenir l'objet Class correspondant à l'interface, mais la classe Class indique spécifiquement qu'il représente des classes et des interfaces dans une application Java. Cependant, l'interface elle-même n'est pas traitée comme une classe et vous ne pouvez donc pas attacher de méthode statique.

Seuls les champs finaux statiques peuvent être déclarés dans une interface (à l'instar des méthodes, qui sont publiques même si vous n'incluez pas le mot clé "public", les champs statiques sont "finaux" avec ou sans le mot clé).

Ce ne sont que des valeurs et seront copiées littéralement où qu'elles soient utilisées au moment de la compilation. Vous ne devez donc jamais "appeler". champs statiques à l'exécution. Avoir une méthode statique n’aurait pas la même sémantique, car cela impliquerait d’appeler une interface sans implémentation, ce que Java ne permet pas.

La raison en est que toutes les méthodes définies dans une interface sont abstraites, que vous déclariez ou non explicitement ce modificateur. Une méthode statique abstraite n'est pas une combinaison de modificateurs autorisée, car les méthodes statiques ne peuvent pas être remplacées.

Pourquoi les interfaces permettent-elles les champs statiques? J'ai le sentiment que cela devrait être considéré comme une "fonctionnalité". La seule possibilité à laquelle je puisse penser serait de regrouper les constantes qui intéresseraient les implémentations de l'interface.

Je conviens que la cohérence aurait été une meilleure approche. Aucun membre statique ne doit être autorisé dans une interface.

Je pense qu'il est possible d'accéder aux méthodes statiques sans créer d'objet et que l'interface ne permet pas de créer un objet afin d'empêcher les programmeurs d'utiliser les méthodes d'interface directement plutôt que de la classe implémentée. Mais si vous définissez une méthode statique dans une interface, vous pouvez y accéder directement sans son implémentation. Ainsi, les méthodes statiques ne sont pas autorisées dans les interfaces. Je ne pense pas que cette cohérence devrait être une préoccupation.

La méthode statique d'interface Java 1.8 est visible pour les méthodes d'interface uniquement, si nous supprimons la méthode methodSta1 () de la classe InterfaceExample, nous ne pourrons pas l’utiliser pour l’objet InterfaceExample. Cependant, comme d'autres méthodes statiques, nous pouvons utiliser des méthodes statiques d'interface en utilisant le nom de la classe. Par exemple, une déclaration valide sera: exp1.methodSta1 ();

Ainsi, après avoir examiné l'exemple ci-dessous, nous pouvons dire: 1) La méthode statique d’interface Java fait partie de l’interface, nous ne pouvons pas l’utiliser pour les objets de classe d’implémentation.

2) Les méthodes statiques d'interface Java sont utiles pour fournir des méthodes utilitaires, par exemple la vérification des valeurs NULL, le tri de la collection, le journal, etc.

3) La méthode statique d'interface Java nous aide à assurer la sécurité en n'autorisant pas les classes d'implémentation (InterfaceExample) à les remplacer.

4) Nous ne pouvons pas définir de méthode statique d’interface pour les méthodes de la classe Object, nous obtiendrons une erreur du compilateur comme suit: «Cette méthode statique ne peut pas masquer la méthode d’instance de Object». Cela est dû au fait qu’il n’est pas autorisé en Java, car Object est la classe de base de toutes les classes et qu’il est impossible d’avoir une méthode statique de niveau classe et une autre méthode d’instance avec la même signature.

5) Nous pouvons utiliser des méthodes statiques d’interface java pour supprimer des classes d’utilitaires telles que Collections et déplacer toutes ses méthodes statiques vers l’interface correspondante.    ce serait facile à trouver et à utiliser.

public class InterfaceExample implements exp1 {

    @Override
    public void method() {
        System.out.println("From method()");
    }

    public static void main(String[] args) {
        new InterfaceExample().method2();
        InterfaceExample.methodSta2();      //  <---------------------------    would not compile
        // methodSta1();                        //  <---------------------------    would not compile
        exp1.methodSta1();
    }

    static void methodSta2() {          //          <-- it compile successfully but it can't be overridden in child classes
        System.out.println("========= InterfaceExample :: from methodSta2() ======");
    }
}


interface exp1 {

    void method();
    //protected void method1();         //          <--      error
    //private void method2();           //          <--      error
    //static void methodSta1();         //          <--      error it require body in java 1.8

    static void methodSta1() {          //          <-- it compile successfully but it can't be overridden in child classes
        System.out.println("========= exp1:: from methodSta1() ======");
    }

    static void methodSta2() {          //          <-- it compile successfully but it can't be overridden in child classes
        System.out.println("========= exp1:: from methodSta2() ======");
    }

    default void method2() { System.out.println("---  exp1:: from method2() ---");}
    //synchronized default void method3() { System.out.println("---");}             // <-- Illegal modifier for the interface method method3; only public, abstract, default, static 
                                                                                // and strictfp are permitted
    //final default void method3() { System.out.println("---");} //             <--      error
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top