web-dev-qa-db-fra.com

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

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ègle 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. 

86
skaffman

Une proposition officielle a été faite pour autoriser les méthodes statiques dans les interfaces de Java 7. Cette proposition est faite sous Project Coin .

Mon opinion personnelle est que c'est une bonne 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 y a plusieurs propositions dans Project Coin qui, j'espère ne feront jamais partie du langage Java, mais celui-ci pourrait être nettoyé. de nombreuses API. Par exemple, la classe Collections a des méthodes statiques pour manipuler n'importe quelle implémentation 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.

49
erickson

Je vais suivre ma théorie de l'animal familier 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 à l'effet que ce soit l'un ou l'autre. .

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 la chose la plus proche des constantes pour Java.

Les classes internes statiques dans les interfaces étaient autorisées car il s'agit 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 entendu, cela pourrait être autorisé dans les futures versions de JLS sans rien casser.

39
skaffman

Il n'est jamais inutile 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 dérouté 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! 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 entendu, 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, la raison pour laquelle vous ne pouvez pas exécuter "result = MyInterface.staticMethod ()" est qu'il devrait exécuter 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.

14
DJClayworth

Le but des interfaces est 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 final fields statiques sont autorisés, qui sont essentiellement des constantes (dans la version 1.5+, 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.

6
Alexander

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 claire:

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

méthodes statiques à autoriser 

vous pouvez ensuite appeler cette méthode à l'aide de 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, par implémentable, je veux dire que vous ne pouvez effectuer aucune opération logique dans le champ, vous pouvez effectuer une opération sur le terrain. Donc, vous ne changez pas le comportement du champ, c'est pourquoi ils sont autorisés.

Les cours intérieurs sont autorisés 

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

J'espère que cela aiderait.

6
user1707035

Il n'y a pas de vraie raison pour ne pas avoir de méthodes statiques dans les interfaces sauf: les concepteurs de langage Java ne le voulaient pas comme ça. 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 ne l’ai pas testé, que vous pouvez "manipuler" du code 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.

3
Angel O'Sphere

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 en tant que 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 d’espace de noms pour les méthodes statiques.

3
nikolavp

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.

Utiliser des outils de cette manière est finalement déroutant.

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

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

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

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

2
Vasil

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 va largement à l'encontre de l'objectif 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 sont censées être.

1
Eli Courtwright

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 (le mot-clé implémente donc, et les interfaces n’étendent pas Object). Comme les interfaces ne sont 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.

1
MetroidFan2002

La méthode statique d’interface Java 1.8 n’est visible que pour les méthodes d’interface. 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 classe. Par exemple, une instruction 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 empêchant les classes d'implémentation (InterfaceExample) de 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”. En effet, cela n’est pas autorisé en Java, car Object est la classe de base de toutes les classes et nous ne pouvons 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
}
0
Ashish Sharma

Seuls les champs finaux statiques peuvent être déclarés dans une interface.

Celles-ci ne sont que des valeurs et seront copiées littéralement où qu'elles soient utilisées lors de la compilation, de sorte que vous n'appelez jamais de 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.

0
cynicalman

Je crois que les méthodes statiques sont accessibles 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 qu'à partir de sa classe implémentée . Mais si vous définissez une méthode statique dans une interface, vous pouvez y accéder directement sans sa mise en œuvre. Ainsi, les méthodes statiques ne sont pas autorisées dans les interfaces. Je ne pense pas que la cohérence devrait être une préoccupation. 

0
prerana gupta

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 un sentiment qui 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.

0
laz