statique - polymorphisme java




Pourquoi ne puis-je pas définir une méthode statique dans une interface Java? (16)

Pourquoi ne puis-je pas définir une méthode statique dans une interface Java?

En fait, vous pouvez en Java 8.

Selon le docs.oracle/staticMethod Java:

Une méthode statique est une méthode associée à la classe dans laquelle elle est définie plutôt qu'à un objet. Chaque instance de la classe partage ses méthodes statiques

En Java 8, une interface peut avoir des méthodes par défaut et des méthodes statiques . Cela nous permet d'organiser plus facilement les méthodes d'assistance dans nos bibliothèques. Nous pouvons garder des méthodes statiques spécifiques à une interface dans la même interface plutôt que dans une classe séparée.

Exemple de méthode par défaut:

list.sort(ordering);

au lieu de

Collections.sort(list, ordering);

Exemple de méthode statique (à partir du docs.oracle/staticMethod lui-même):

public interface TimeClient {
    // ...
    static public ZoneId getZoneId (String zoneString) {
        try {
            return ZoneId.of(zoneString);
        } catch (DateTimeException e) {
            System.err.println("Invalid time zone: " + zoneString +
                "; using default time zone instead.");
            return ZoneId.systemDefault();
        }
    }

    default public ZonedDateTime getZonedDateTime(String zoneString) {
        return ZonedDateTime.of(getLocalDateTime(), getZoneId(zoneString));
    }    
}

Voici l'exemple:

public interface IXMLizable<T>
{
  static T newInstanceFromXML(Element e);
  Element toXMLElement();
}

Bien sûr, cela ne fonctionnera pas. Mais pourquoi pas?

L'un des problèmes possibles serait, que se passe-t-il lorsque vous appelez:

IXMLizable.newInstanceFromXML(e);

Dans ce cas, je pense qu'il devrait simplement appeler une méthode vide (ie {}). Toutes les sous-classes seraient obligées d'implémenter la méthode statique, donc tout irait bien quand on appelle la méthode statique. Alors pourquoi n'est-ce pas possible?

EDIT: Je suppose que je cherche une réponse plus profonde que "parce que c'est comme Java".

Y a-t-il une raison technologique particulière pour laquelle les méthodes statiques ne peuvent pas être écrasées? Autrement dit, pourquoi les concepteurs de Java ont-ils décidé de rendre les méthodes d'instance substituables, mais pas les méthodes statiques?

EDIT: Le problème avec ma conception est que j'essaie d'utiliser des interfaces pour appliquer une convention de codage.

Autrement dit, le but de l'interface est double:

  1. Je veux que l'interface IXMLizable me permette de convertir des classes qui l'implémentent en éléments XML (en utilisant le polymorphisme, ça marche bien).

  2. Si quelqu'un veut créer une nouvelle instance d'une classe qui implémente l'interface IXMLizable, il saura toujours qu'il y aura un constructeur static newInstanceFromXML (Element e).

Y a-t-il un autre moyen d'assurer cela, autre que de simplement ajouter un commentaire dans l'interface?

EDIT: A partir de Java 8, les méthodes statiques sont désormais autorisées dans les interfaces.


Java 8 autorise les méthodes d'interface statiques

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:

  1. Pourquoi, dans le mauvais vieux temps, les interfaces ne pouvaient-elles pas contenir des méthodes statiques?
  2. Pourquoi les méthodes statiques ne peuvent-elles pas être surchargées?

Méthodes statiques dans les interfaces

Il n'y avait pas de raison technique forte pour laquelle les interfaces ne pouvaient pas avoir de méthodes statiques dans les versions précédentes. Cela se résume bien à l'affiche d'une question en double. Les méthodes d'interface statiques ont d'abord été considérées comme un petit changement de langage, et ensuite il y avait une proposition officielle pour les ajouter dans Java 7, mais elle a été abandonnée plus tard en raison de complications imprévues.

Enfin, Java 8 a introduit des méthodes d'interface statiques, ainsi que des méthodes d'instance remplaçables avec une implémentation par défaut. Ils ne peuvent toujours pas avoir de champs d'instance. Ces fonctionnalités font partie du support d'expression lambda, et vous pouvez en lire plus à leur sujet dans la partie H de JSR 335.

Surcharger les méthodes statiques

La réponse à la deuxième question est un peu plus compliquée.

Les méthodes statiques sont résolvables au moment de la compilation. L'envoi dynamique a un sens 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 à invoquer. Mais l'invocation d'une méthode statique nécessite une classe, et puisque cette classe est connue statiquement - au moment de la compilation - l'envoi dynamique est inutile.

Un peu de contexte sur le fonctionnement des méthodes d'instance est nécessaire pour comprendre ce qui se passe ici. Je suis sûr que la mise en œuvre réelle est assez différente, mais permettez-moi d'expliquer ma notion de répartition des méthodes, qui modélise le comportement observé avec précision.

Imaginons que chaque classe possède une table de hachage qui mappe les signatures de méthode (nom et types de paramètre) à un morceau de code réel pour implémenter la méthode. Lorsque la machine virtuelle tente d'invoquer 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 est répétée ici. Cela se poursuit jusqu'à ce que la méthode soit trouvée ou qu'il n'y ait plus de classes parentes, ce qui entraîne une NoSuchMethodError .

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 - c'est un "override".

Supposons maintenant que nous ignorions l'instance de l'objet et que nous commencions par une sous-classe. La résolution pourrait se dérouler comme ci-dessus, vous donnant une sorte de méthode statique "overridable". La résolution peut cependant se produire à la compilation, car le compilateur commence à partir d'une classe connue, plutôt que d'attendre l'exécution pour interroger un objet d'un type non spécifié pour sa classe. Il est inutile de "surcharger" une méthode statique puisque l'on peut toujours spécifier la classe qui contient la version désirée.

Constructeur "interfaces"

Voici un peu plus de matériel pour répondre à la récente modification de la question.

Il semble que vous vouliez mandater efficacement une méthode de type constructeur pour chaque implémentation de IXMLizable . Oubliez d'essayer d'imposer 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);

Puisque vous devez nommer explicitement le type concret Foo lors de la "construction" du nouvel objet, le compilateur peut vérifier qu'il possède effectivement la méthode usine nécessaire. Et si ce n'est pas le cas, alors quoi? Si je peux implémenter un IXMLizable qui n'a pas le "constructeur", et je crée une instance et la passe à votre code, c'est un IXMLizable avec toute l'interface nécessaire.

La construction fait partie de la mise en œuvre, 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.


Avec l'avènement de Java 8, il est maintenant possible d'écrire des méthodes par défaut et statiques dans l'interface. docs.oracle/staticMethod

Par exemple:

public interface Arithmetic {

    public int add(int a, int b);

    public static int multiply(int a, int b) {
        return a * b;
    }
}
public class ArithmaticImplementation implements Arithmetic {

    @Override
    public int add(int a, int b) {
        return a + b;
    }

    public static void main(String[] args) {
        int result = Arithmetic.multiply(2, 3);
        System.out.println(result);
    }
}

Résultat : 6

ASTUCE: L' appel d'une méthode d'interface statique ne nécessite aucune implémentation par une classe. Sûrement, cela arrive parce que les mêmes règles pour les méthodes statiques dans les superclasses s'appliquent aux méthodes statiques sur les interfaces.


Cela a déjà été demandé et répondu, here

Pour dupliquer ma réponse:

Il n'est jamais utile de déclarer une méthode statique dans une interface. Ils ne peuvent pas être exécutés par l'appel normal MyInterface.staticMethod (). Si vous les appelez en spécifiant la classe d'implémentation MyImplementor.staticMethod (), vous devez connaître la classe réelle, donc peu importe que l'interface la contienne ou non.

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

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

les règles pour static disent 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.

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 pas y avoir de version définie dans MyInterface, car c'est une interface. Il n'a pas de code par définition.

Alors que vous pouvez dire que cela revient à «parce que Java le fait de cette façon», en réalité, la décision est une conséquence logique d'autres décisions de conception, également faites pour une très bonne raison.


Commentant EDIT: As of Java 8, static methods are now allowed in interfaces.

C'est vrai, les méthodes statiques depuis Java 8 sont autorisées dans les interfaces, mais votre exemple ne fonctionnera toujours pas. Vous ne pouvez pas simplement définir une méthode statique: vous devez l'implémenter ou vous obtiendrez une erreur de compilation.


Eh bien, sans génériques, les interfaces statiques sont inutiles car tous les appels de méthodes statiques sont résolus au moment de la compilation. Donc, il n'y a pas d'utilisation réelle pour eux.

Avec les génériques, ils ont une utilisation - avec ou sans implémentation par défaut. De toute évidence, il devrait y avoir une dérogation et ainsi de suite. Cependant, je suppose que cette utilisation n'était pas très OO (comme les autres réponses le soulignent) et que, par conséquent, elle ne valait pas l'effort dont ils auraient besoin pour l'utiliser utilement.


Les interfaces fournissent simplement une liste de choses qu'une classe fournira, pas une implémentation réelle de ces choses, qui est ce que votre élément statique est.

Si vous voulez des statistiques, utilisez une classe abstraite et héritez-la, sinon, supprimez la statique.

J'espère que cela pourra aider!


Les interfaces sont concernées par le polymorphisme qui est intrinsèquement lié aux instances d'objets, pas aux classes. Donc statique n'a pas de sens dans le contexte d'une interface.


Normalement, cela est fait en utilisant un motif d'usine

public interface IXMLizableFactory<T extends IXMLizable> {
  public T newInstanceFromXML(Element e);
}

public interface IXMLizable {
  public Element toXMLElement();
}

Plusieurs réponses ont discuté des problèmes avec le concept de méthodes statiques substituables. Cependant, parfois, vous rencontrez un motif où il semble que ce soit juste ce que vous voulez utiliser.

Par exemple, je travaille avec une couche objet-relationnelle qui a des objets de valeur, mais aussi des commandes pour manipuler les objets de valeur. Pour diverses raisons, chaque classe d'objets valeur doit définir des méthodes statiques qui permettent au framework de trouver l'instance de commande. Par exemple, pour créer une personne que vous souhaitez faire:

cmd = createCmd(Person.getCreateCmdId());
Person p = cmd.execute();

et pour charger une personne par ID que vous feriez

cmd = createCmd(Person.getGetCmdId());
cmd.set(ID, id);
Person p = cmd.execute();

C'est assez pratique, mais il a ses problèmes; notamment l'existence des méthodes statiques ne peut pas être appliquée dans l'interface. Une méthode statique remplaçable dans l'interface serait exactement ce dont nous aurions besoin, si seulement cela pouvait fonctionner d'une manière ou d'une autre.

Les EJB résolvent ce problème en ayant une interface Home; chaque objet sait comment trouver son Home et le Home contient les méthodes "statiques". De cette façon, les méthodes "statiques" peuvent être remplacées si nécessaire, et vous n'encombrez pas l'interface normale (appelée "Remote") avec des méthodes qui ne s'appliquent pas à une instance de votre bean. Faites simplement en sorte que l'interface normale spécifie une méthode "getHome ()". Renvoyer une instance de l'objet Home (qui pourrait être un singleton, je suppose) et l'appelant peut effectuer des opérations qui affectent tous les objets Person.


Premièrement, toutes les décisions linguistiques sont des décisions prises par les créateurs de langue. Il n'y a rien dans le monde du génie logiciel ou de la définition de langage ou de l'écriture de compilateur / interprète qui dit qu'une méthode statique ne peut pas faire partie d'une interface. J'ai créé un couple de langages et de compilateurs écrits pour eux - il suffit de s'asseoir et de définir une sémantique significative. Je dirais que la sémantique d'une méthode statique dans une interface est remarquablement claire - même si le compilateur doit reporter la résolution de la méthode à l'exécution.

Deuxièmement, nous utilisons des méthodes statiques à tous les moyens, il y a une raison valable pour avoir un modèle d'interface qui inclut des méthodes statiques - je ne peux parler pour aucun d'entre vous, mais j'utilise des méthodes statiques sur une base régulière.

La réponse correcte la plus probable est qu'il n'y avait pas de besoin perçu, au moment de la définition du langage, pour les méthodes statiques dans les interfaces. Java a beaucoup évolué au cours des années et c'est un élément qui a apparemment gagné un certain intérêt. Le fait qu'il ait été examiné pour Java 7 indique qu'il a atteint un niveau d'intérêt susceptible d'entraîner un changement de langue. Pour ma part, je serai heureux quand je n'aurai plus besoin d'instancier un objet juste pour pouvoir appeler ma méthode getter non statique pour accéder à une variable statique dans une instance de sous-classe ...


Quelque chose qui pourrait être implémenté est l'interface statique (au lieu de la méthode statique dans une interface). Toutes les classes implémentant une interface statique donnée doivent implémenter les méthodes statiques correspondantes. Vous pouvez obtenir l'interface statique SI de n'importe quel clazz de classe en utilisant

SI si = clazz.getStatic(SI.class); // null if clazz doesn't implement SI
// alternatively if the class is known at compile time
SI si = Someclass.static.SI; // either compiler errror or not null

alors vous pouvez appeler si.method(params) . Cela serait utile (pour le modèle de conception d'usine par exemple) parce que vous pouvez obtenir (ou vérifier l'implémentation de) l'implémentation des méthodes statiques SI à partir d'une classe inconnue de compilation! Une répartition dynamique est nécessaire et vous pouvez remplacer les méthodes statiques (si ce n'est définitives) d'une classe en l'étendant (lorsqu'elle est appelée via l'interface statique). Évidemment, ces méthodes peuvent uniquement accéder aux variables statiques de leur classe.


Supposons que vous puissiez le faire. Considérez cet exemple:

interface Iface {
  public static void thisIsTheMethod();
}

class A implements Iface {

  public static void thisIsTheMethod(){
    system.out.print("I'm class A");
  }

}

class B extends Class A {

  public static void thisIsTheMethod(){
    System.out.print("I'm class B");
  } 
}

SomeClass {

  void doStuff(Iface face) {
    IFace.thisIsTheMethod();
    // now what would/could/should happen here.
  }

}

Une interface ne peut jamais être déréférencée statiquement, par exemple ISomething.member . Une interface est toujours déréférencée via une variable qui fait référence à une instance d'une sous-classe de l'interface. Ainsi, une référence d'interface ne peut jamais savoir à quelle sous-classe elle fait référence sans une instance de sa sous-classe.

Ainsi, l'approximation la plus proche d'une méthode statique dans une interface serait une méthode non statique qui ignore "ceci", c'est-à-dire qui n'accède à aucun membre non statique de l'instance. Au niveau de l'abstraction de bas niveau, toute méthode non statique (après recherche dans n'importe quelle vtable) est vraiment juste une fonction avec la portée de classe qui prend "this" comme un paramètre formel implicite. Voir l'objet singleton de Scala et l'interopérabilité avec Java comme preuve de ce concept. Et donc toute méthode statique est une fonction avec une portée de classe qui ne prend pas un paramètre "this". Ainsi, normalement, une méthode statique peut être appelée statiquement, mais comme indiqué précédemment, une interface n'a aucune implémentation (est abstraite).

Ainsi, pour obtenir une approximation plus proche d'une méthode statique dans une interface, vous devez utiliser une méthode non statique, puis n'accéder à aucun des membres d'instance non statiques. Il n'y aurait aucun avantage de performance possible d'une autre manière, car il n'y a aucun moyen de lier statiquement (au moment de la compilation) quelque ISomething.member() . Le seul avantage que je vois d'une méthode statique dans une interface est qu'elle n'entrerait pas (c'est-à-dire ignore) un "ceci" implicite et donc interdirait l'accès à n'importe lequel des membres d'instance non-statiques. Cela déclarerait implicitement que la fonction qui n'accède pas à "this", est immutate et même pas en lecture seule par rapport à sa classe contenant. Mais une déclaration de "static" dans une interface ISomething pourrait aussi ISomething gens qui tenteraient d'y accéder avec ISomething.member() chose d' ISomething.member() qui provoquerait une erreur de compilation. Je suppose que si l'erreur du compilateur était suffisamment explicative, il serait préférable d'essayer d'éduquer les gens sur l'utilisation d'une méthode non statique pour accomplir ce qu'ils veulent (principalement des méthodes d'usine), comme nous le faisons ici. Q & A fois sur ce site), donc c'est évidemment un problème qui n'est pas intuitif pour beaucoup de gens. J'ai dû y réfléchir pendant un moment pour avoir la bonne compréhension.

La façon d'obtenir un champ statique mutable dans une interface est d'utiliser des méthodes getter et setter non statiques dans une interface, pour accéder à ce champ statique qui se trouve dans la sous-classe. Sidenote, statique apparemment immuable peut être déclaré dans une interface Java avec static final .


Quel est le besoin de la méthode statique dans l'interface, les méthodes statiques sont utilisées essentiellement lorsque vous n'avez pas besoin de créer une instance d'objet idée globale de l'interface est d'apporter des concepts OOP avec l'introduction de la méthode statique que vous détournez du concept.


Why can't I define a static method in a Java interface?

Toutes les méthodes d'une interface sont explicitement abstraites et vous ne pouvez donc pas les définir comme statiques car les méthodes statiques ne peuvent pas être abstraites.





static-methods