Pourquoi Java ne permet-il pas de remplacer les méthodes statiques?


534

Pourquoi n'est-il pas possible de remplacer les méthodes statiques?

Si possible, veuillez utiliser un exemple.


3
La plupart des langues OOP ne le permettent pas.
jmucchiello

7
@jmucchiello: voir ma réponse. Je pensais la même chose que vous, mais j'ai ensuite appris les méthodes de la «classe» Ruby / Smalltalk et il y a donc d'autres vrais langages OOP qui font cela.
Kevin Brock

5
@jmucchiello la plupart des langages OOP ne sont pas de vrais langages OOP (je pense à Smalltalk)
mathk


1
peut être dû au fait que Java résout les appels aux méthodes statiques au moment de la compilation. Donc, même si vous avez écrit Parent p = new Child()et que p.childOverriddenStaticMethod()le compilateur le résoudra Parent.childOverriddenStaticMethod()en regardant le type de référence.
Manoj

Réponses:


494

La substitution dépend de la présence d'une instance d'une classe. Le point de polymorphisme est que vous pouvez sous-classer une classe et les objets implémentant ces sous-classes auront des comportements différents pour les mêmes méthodes définies dans la superclasse (et remplacées dans les sous-classes). Une méthode statique n'est associée à aucune instance d'une classe, le concept n'est donc pas applicable.

Il y avait deux considérations guidant la conception de Java qui ont eu un impact sur cela. L'un était un problème de performances: il y avait eu beaucoup de critiques à propos de Smalltalk sur le fait qu'il était trop lent (la collecte des ordures et les appels polymorphes en faisaient partie) et les créateurs de Java étaient déterminés à éviter cela. Une autre décision a été que le public cible de Java était les développeurs C ++. Faire fonctionner les méthodes statiques de la façon dont elles fonctionnaient avait l'avantage d'être familier pour les programmeurs C ++ et était également très rapide, car il n'est pas nécessaire d'attendre le temps d'exécution pour déterminer quelle méthode appeler.


18
... mais seulement "correct" en Java. Par exemple, l'équivalent de Scala des "classes statiques" (qui sont appelées objects) permet la surcharge des méthodes.

32
Objective-C permet également de remplacer les méthodes de classe.
Richard

11
Il existe une hiérarchie de type à la compilation et une hiérarchie de type à l'exécution. Il est parfaitement logique de demander pourquoi un appel de méthode statique ne se prévaut pas de la hiérarchie de type d'exécution dans les circonstances où il y en a une. En Java, cela se produit lors de l'appel d'une méthode statique à partir d'un objet ( obj.staticMethod()) - ce qui est autorisé et utilise les types au moment de la compilation. Lorsque l'appel statique se trouve dans une méthode non statique d'une classe, l'objet "courant" peut être un type dérivé de la classe - mais les méthodes statiques définies sur les types dérivés ne sont pas prises en compte (elles sont dans le type d'exécution hiérarchie).
Steve Powell

18
J'aurais dû préciser: il n'est pas vrai que le concept ne s'applique pas .
Steve Powell

13
Cette réponse, bien que correcte, s'apparente davantage à «comment c'est» plutôt qu'à ce qu'elle devrait être ou plus exactement comment elle pourrait être afin de répondre aux attentes du PO et, par conséquent, ici, à moi-même et aux autres. Il n'y a aucune raison concrète de ne pas autoriser la substitution de méthodes statiques autres que "c'est comme ça". Je pense que c'est un défaut personnellement.
RichieHH

186

Personnellement, je pense que c'est une faille dans la conception de Java. Oui, oui, je comprends que les méthodes non statiques sont attachées à une instance tandis que les méthodes statiques sont attachées à une classe, etc. etc. Toujours considérer le code suivant:

public class RegularEmployee {
    private BigDecimal salary;

    public void setSalary(BigDecimal salary) {
        this.salary = salary;
    }

    public static BigDecimal getBonusMultiplier() {
        return new BigDecimal(".02");
    }

    public BigDecimal calculateBonus() {
        return salary.multiply(getBonusMultiplier());
    }

    /* ... presumably lots of other code ... */
}

public class SpecialEmployee extends RegularEmployee {
    public static BigDecimal getBonusMultiplier() {
        return new BigDecimal(".03");
    }
}

Ce code ne fonctionnera pas comme vous pouvez vous y attendre. À savoir, SpecialEmployee obtient un bonus de 2% tout comme les employés réguliers. Mais si vous supprimez les "statiques", alors SpecialEmployee obtient un bonus de 3%.

(Certes, cet exemple est un mauvais style de codage dans la mesure où, dans la vraie vie, vous voudriez probablement que le multiplicateur de bonus soit dans une base de données quelque part plutôt que codé en dur. Mais c'est simplement parce que je ne voulais pas embêter l'exemple avec beaucoup de code hors de propos.)

Il me semble tout à fait plausible que vous souhaitiez rendre statique getBonusMultiplier. Vous souhaitez peut-être pouvoir afficher le multiplicateur de bonus pour toutes les catégories d'employés, sans avoir besoin d'avoir une instance d'un employé dans chaque catégorie. Quel serait l'intérêt de rechercher de tels exemples? Que se passe-t-il si nous créons une nouvelle catégorie d'employés et qu'aucun employé ne lui est encore affecté? Il s'agit logiquement d'une fonction statique.

Mais ça ne marche pas.

Et oui, oui, je peux penser à un certain nombre de façons de réécrire le code ci-dessus pour le faire fonctionner. Mon point n'est pas qu'il crée un problème insoluble, mais qu'il crée un piège pour le programmeur imprudent, car le langage ne se comporte pas comme je pense qu'une personne raisonnable pourrait s'y attendre.

Peut-être que si j'essayais d'écrire un compilateur pour un langage OOP, je verrais rapidement pourquoi l'implémenter afin que les fonctions statiques puissent être remplacées serait difficile, voire impossible.

Ou peut-être y a-t-il une bonne raison pour laquelle Java se comporte de cette façon. Quelqu'un peut-il signaler un avantage à ce comportement, une catégorie de problème facilitée par cela? Je veux dire, ne me pointez pas simplement vers la spécification du langage Java et dites "voyez, c'est documenté comment ça se comporte". Je le sais. Mais y a-t-il une bonne raison pour laquelle il DEVRAIT se comporter de cette façon? (En plus de l'évidence "faire fonctionner correctement était trop difficile" ...)

Mise à jour

@VicKirk: Si vous voulez dire qu'il s'agit d'une "mauvaise conception" car elle ne correspond pas à la façon dont Java gère la statique, ma réponse est "Eh bien, duh, bien sûr." Comme je l'ai dit dans mon article d'origine, cela ne fonctionne pas. Mais si vous voulez dire que c'est une mauvaise conception dans le sens où il y aurait quelque chose de fondamentalement mauvais avec un langage où cela fonctionne, c'est-à-dire où la statique pourrait être remplacée tout comme les fonctions virtuelles, que cela introduirait en quelque sorte une ambiguïté ou qu'il serait impossible de mettre en œuvre efficacement ou certains, je réponds: "Pourquoi? Quel est le problème avec le concept?"

Je pense que l'exemple que je donne est une chose très naturelle à vouloir faire. J'ai une classe qui a une fonction qui ne dépend d'aucune donnée d'instance, et que je pourrais très raisonnablement vouloir appeler indépendamment d'une instance, ainsi que vouloir appeler à partir d'une méthode d'instance. Pourquoi cela ne fonctionnerait-il pas? J'ai rencontré cette situation un bon nombre de fois au fil des ans. En pratique, je la contourne en rendant la fonction virtuelle, puis en créant une méthode statique dont le seul but dans la vie est d'être une méthode statique qui transmet l'appel à la méthode virtuelle avec une instance factice. Cela semble être un moyen très détourné pour y arriver.


11
@Bemrose: Mais c'est mon point: pourquoi ne devrais-je pas être autorisé à le faire? Peut-être que mon concept intuitif de ce qu'une "statique" devrait faire est différent du vôtre, mais en gros je pense à une statique comme une méthode qui PEUT être statique car elle n'utilise aucune donnée d'instance, et qui DEVRAIT être statique parce que vous voudrez peut-être pour l'appeler indépendamment d'une instance. Une statique est clairement liée à une classe: je m'attends à ce que Integer.valueOf soit lié à Integers et Double.valueOf soit lié à Doubles.
Jay

9
@ewernli & Bemrose: Oui oui, c'est comme ça. Je ne discute pas de cela. Comme le code de mon exemple ne fonctionne pas, bien sûr, je n'essaierais pas de l'écrire. Ma question est de savoir pourquoi il en est ainsi. (Je crains que cela ne se transforme en une de ces conversations où nous ne communiquons tout simplement pas. "Excusez-moi M. Salesman, puis-je en obtenir une en rouge?" "Non, cela coûte 5 $." "Oui, je sais que cela coûte 5 $, mais puis-je en obtenir un rouge? "" Monsieur, je viens de vous dire que cela coûte 5 $. "" D'accord, je connais le prix, mais je posais des questions sur la couleur. "" Je vous ai déjà dit le prix! " etc.)
Jay

6
Je pense que finalement ce code prête à confusion. Vérifiez si l'instance est passée en tant que paramètre. Vous dites ensuite que l'instance d'exécution doit dicter la méthode statique appelée. Cela rend fondamentalement toute une hiérarchie distincte parallèle à l'instance existante. Et maintenant, si une sous-classe définit la même signature de méthode que non statique? Je pense que les règles rendraient les choses assez compliquées. C'est précisément ce genre de complications de langage que Java essaie d'éviter.
Yishai

6
@Yishai: RE "L'instance d'exécution dicte la méthode statique appelée": Exactement. Je ne vois pas pourquoi vous ne pouvez rien faire avec un statique que vous pouvez faire avec un virtuel. "Hiérarchie distincte": je dirais, faites-en partie de la même hiérarchie. Pourquoi la statique n'est-elle pas incluse dans la même hiérarchie? "La sous-classe définit la même signature non statique": je présume que ce serait illégal, tout comme il est illégal d'avoir, par exemple, une sous-classe qui remplace une fonction avec une signature identique mais un type de retour différent, ou de ne pas lever toutes les exceptions qui le parent lance, ou pour avoir une portée plus étroite.
Jay

28
Je pense que Jay a raison - cela m'a aussi surpris quand j'ai découvert que la statique ne pouvait pas être outrepassée. En partie parce que si j'ai A avec la méthode someStatic()et B étend A, puis B.someMethod() se lie à la méthode en A. Si j'ajoute ensuite someStatic()à B, le code appelant invoque toujours A.someStatic()jusqu'à ce que je recompile le code appelant. De plus, cela m'a surpris d' bInstance.someStatic()utiliser le type déclaré de bInstance, pas le type d'exécution car il se lie à la compilation et non au lien, A bInstance; ... bInstance.someStatic()invoque donc A.someStatic () si si B.someStatic () existe.
Lawrence Dol

42

La réponse courte est: c'est tout à fait possible, mais Java ne le fait pas.

Voici un code qui illustre l' état actuel des choses à Java:

Fichier Base.java:

package sp.trial;
public class Base {
  static void printValue() {
    System.out.println("  Called static Base method.");
  }
  void nonStatPrintValue() {
    System.out.println("  Called non-static Base method.");
  }
  void nonLocalIndirectStatMethod() {
    System.out.println("  Non-static calls overridden(?) static:");
    System.out.print("  ");
    this.printValue();
  }
}

Fichier Child.java:

package sp.trial;
public class Child extends Base {
  static void printValue() {
    System.out.println("  Called static Child method.");
  }
  void nonStatPrintValue() {
    System.out.println("  Called non-static Child method.");
  }
  void localIndirectStatMethod() {
    System.out.println("  Non-static calls own static:");
    System.out.print("  ");
    printValue();
  }
  public static void main(String[] args) {
    System.out.println("Object: static type Base; runtime type Child:");
    Base base = new Child();
    base.printValue();
    base.nonStatPrintValue();
    System.out.println("Object: static type Child; runtime type Child:");
    Child child = new Child();
    child.printValue();
    child.nonStatPrintValue();
    System.out.println("Class: Child static call:");
    Child.printValue();
    System.out.println("Class: Base static call:");
    Base.printValue();
    System.out.println("Object: static/runtime type Child -- call static from non-static method of Child:");
    child.localIndirectStatMethod();
    System.out.println("Object: static/runtime type Child -- call static from non-static method of Base:");
    child.nonLocalIndirectStatMethod();
  }
}

Si vous exécutez ceci (je l'ai fait sur un Mac, depuis Eclipse, en utilisant Java 1.6), vous obtenez:

Object: static type Base; runtime type Child.
  Called static Base method.
  Called non-static Child method.
Object: static type Child; runtime type Child.
  Called static Child method.
  Called non-static Child method.
Class: Child static call.
  Called static Child method.
Class: Base static call.
  Called static Base method.
Object: static/runtime type Child -- call static from non-static method of Child.
  Non-static calls own static.
    Called static Child method.
Object: static/runtime type Child -- call static from non-static method of Base.
  Non-static calls overridden(?) static.
    Called static Base method.

Ici, les seuls cas qui pourraient être une surprise (et sur lesquels porte la question) semblent être le premier cas:

"Le type d'exécution n'est pas utilisé pour déterminer quelles méthodes statiques sont appelées, même lorsqu'il est appelé avec une instance d'objet ( obj.staticMethod())."

et le dernier cas:

"Lors de l'appel d'une méthode statique à partir d'une méthode objet d'une classe, la méthode statique choisie est celle accessible depuis la classe elle-même et non depuis la classe définissant le type d'exécution de l'objet."

Appel avec une instance d'objet

L'appel statique est résolu au moment de la compilation, tandis qu'un appel de méthode non statique est résolu au moment de l'exécution. Notez que bien que les méthodes statiques soient héritées (du parent), elles ne sont pas remplacées (par l'enfant). Cela pourrait être une surprise si vous vous attendiez à autre chose.

Appel depuis une méthode objet

Les appels de méthode objet sont résolus à l'aide du type d'exécution, mais les appels de méthode statique ( classe ) sont résolus à l'aide du type de compilation (déclaré).

Changer les règles

Pour modifier ces règles, de sorte que le dernier appel dans l'exemple appelé Child.printValue(), les appels statiques doivent être fournis avec un type au moment de l'exécution, plutôt que le compilateur résolvant l'appel au moment de la compilation avec la classe déclarée de l'objet (ou le contexte). Les appels statiques pourraient alors utiliser la hiérarchie de type (dynamique) pour résoudre l'appel, tout comme les appels de méthode objet le font aujourd'hui.

Cela serait facilement réalisable (si nous changions Java: -O), et ce n'est pas du tout déraisonnable, cependant, cela a quelques considérations intéressantes.

La considération principale est que nous devons décider quels appels de méthodes statiques devraient faire cela.

À l'heure actuelle, Java a cette "bizarrerie" dans le langage où les obj.staticMethod()appels sont remplacés par des ObjectClass.staticMethod()appels (normalement avec un avertissement). [ Remarque: ObjectClass est le type de compilation obj.] Ce sont de bons candidats pour remplacer de cette manière, en prenant le type d'exécution de obj.

Si nous le faisions, cela rendrait les corps de méthodes plus difficiles à lire: les appels statiques dans une classe parente pourraient potentiellement être "réacheminés" dynamiquement . Pour éviter cela, nous devrions appeler la méthode statique avec un nom de classe - et cela rend les appels résolus de manière plus évidente avec la hiérarchie de type à la compilation (comme maintenant).

Les autres façons d'invoquer une méthode statique sont plus délicates: this.staticMethod()devraient signifier la même chose que obj.staticMethod(), en prenant le type d'exécution de this. Cependant, cela peut provoquer des maux de tête avec les programmes existants, qui appellent des méthodes statiques (apparemment locales) sans décoration (ce qui est sans doute équivalent à this.method()).

Qu'en est-il des appels sans fioritures staticMethod()? Je suggère qu'ils fassent de même qu'aujourd'hui et utilisent le contexte de classe local pour décider quoi faire. Sinon, une grande confusion s'ensuivrait. Bien sûr, cela signifie que method()cela signifierait this.method()si methodc'était une méthode non statique et ThisClass.method()si methodc'était une méthode statique. Ceci est une autre source de confusion.

Autres considérations

Si nous changions ce comportement (et rendions des appels statiques potentiellement non locaux dynamiques), nous voudrions probablement revoir la signification de final, privateet en protectedtant que qualificatifs sur les staticméthodes d'une classe. Nous devrons alors tous nous habituer au fait que les méthodes private staticet public finalne sont pas remplacées et peuvent donc être résolues en toute sécurité au moment de la compilation et sont "sûres" à lire comme références locales.


"Si nous le faisions, cela rendrait les corps de méthodes plus difficiles à lire: les appels statiques dans une classe parente pourraient potentiellement être" réacheminés "dynamiquement." C'est vrai, mais c'est exactement ce qui se passe maintenant avec les appels de fonction non statiques ordinaires. Ceci est régulièrement présenté comme une caractéristique positive pour les fonctions virtuelles ordinaires, pas comme un problème.
Jay

25

En fait, nous avions tort.
Bien que Java ne vous permette pas de remplacer les méthodes statiques par défaut, si vous examinez attentivement la documentation des classes Class et Method en Java, vous pouvez toujours trouver un moyen d'émuler les méthodes statiques en remplaçant la solution de contournement suivante:

import java.lang.reflect.InvocationTargetException;
import java.math.BigDecimal;

class RegularEmployee {

    private BigDecimal salary = BigDecimal.ONE;

    public void setSalary(BigDecimal salary) {
        this.salary = salary;
    }
    public static BigDecimal getBonusMultiplier() {
        return new BigDecimal(".02");
    }
    public BigDecimal calculateBonus() {
        return salary.multiply(this.getBonusMultiplier());
    }
    public BigDecimal calculateOverridenBonus() {
        try {
            // System.out.println(this.getClass().getDeclaredMethod(
            // "getBonusMultiplier").toString());
            try {
                return salary.multiply((BigDecimal) this.getClass()
                    .getDeclaredMethod("getBonusMultiplier").invoke(this));
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        }
        return null;
    }
    // ... presumably lots of other code ...
}

final class SpecialEmployee extends RegularEmployee {

    public static BigDecimal getBonusMultiplier() {
        return new BigDecimal(".03");
    }
}

public class StaticTestCoolMain {

    static public void main(String[] args) {
        RegularEmployee Alan = new RegularEmployee();
        System.out.println(Alan.calculateBonus());
        System.out.println(Alan.calculateOverridenBonus());
        SpecialEmployee Bob = new SpecialEmployee();
        System.out.println(Bob.calculateBonus());
        System.out.println(Bob.calculateOverridenBonus());
    }
}

Sortie résultante:

0.02
0.02
0.02
0.03

ce que nous essayions de réaliser :)

Même si nous déclarons la troisième variable Carl comme RegularEmployee et lui assignons une instance de SpecialEmployee, nous aurons toujours un appel de la méthode RegularEmployee dans le premier cas et un appel de la méthode SpecialEmployee dans le second cas

RegularEmployee Carl = new SpecialEmployee();

System.out.println(Carl.calculateBonus());
System.out.println(Carl.calculateOverridenBonus());

il suffit de regarder la console de sortie:

0.02
0.03

;)


9
Oui, la réflexion est à peu près la seule chose que l'on puisse faire - mais la question n'est pas exactement celle-ci - utile de l'avoir ici cependant
Mr_and_Mrs_D

1
Cette réponse est le plus gros hack que j'ai vu jusqu'à présent sur tous les sujets Java. C'était amusant de le lire encore :)
Andrejs

19

Les méthodes statiques sont traitées comme globales par la JVM, elles ne sont pas du tout liées à une instance d'objet.

Conceptuellement, cela pourrait être possible si vous pouviez appeler des méthodes statiques à partir d'objets de classe (comme dans des langages comme Smalltalk) mais ce n'est pas le cas en Java.

ÉDITER

Vous pouvez surcharger la méthode statique, c'est ok. Mais vous ne pouvez pas remplacer une méthode statique, car les classes ne sont pas des objets de première classe. Vous pouvez utiliser la réflexion pour obtenir la classe d'un objet au moment de l'exécution, mais l'objet que vous obtenez ne correspond pas à la hiérarchie des classes.

class MyClass { ... }
class MySubClass extends MyClass { ... }

MyClass obj1 = new MyClass();
MySubClass obj2 = new MySubClass();

ob2 instanceof MyClass --> true

Class clazz1 = obj1.getClass();
Class clazz2 = obj2.getClass();

clazz2 instanceof clazz1 --> false

Vous pouvez réfléchir sur les classes, mais cela s'arrête là. Vous n'appelez pas une méthode statique en utilisant clazz1.staticMethod(), mais en utilisant MyClass.staticMethod(). Une méthode statique n'est pas liée à un objet et il n'y a donc aucune notion de thisni superdans une méthode statique. Une méthode statique est une fonction globale; en conséquence, il n'y a pas non plus de notion de polymorphisme et, par conséquent, le dépassement de méthode n'a aucun sens.

Mais cela pourrait être possible s'il MyClasss'agissait d'un objet au moment de l'exécution sur lequel vous appelez une méthode, comme dans Smalltalk (ou peut-être JRuby comme un commentaire le suggère, mais je ne sais rien de JRuby).

Oh ouais ... encore une chose. Vous pouvez invoquer une méthode statique via un objet obj1.staticMethod()mais ce sucre vraiment syntaxique pour MyClass.staticMethod()et doit être évité. Il déclenche généralement un avertissement dans l'IDE moderne. Je ne sais pas pourquoi ils ont autorisé ce raccourci.


5
Même de nombreux langages modernes comme Ruby ont des méthodes de classe et permettent de les remplacer.
Chandra Sekar

3
Les classes existent en tant qu'objets en Java. Voir la classe "Classe". Je peux dire myObject.getClass () et il me renverra une instance de l'objet de classe approprié.
Jay

5
Vous obtenez seulement une "description" de la classe - pas la classe elle-même. Mais la différence est subtile.
ewernli

Vous avez toujours de la classe mais elle est cachée dans la VM (près du chargeur de classe), l'utilisateur n'y a presque pas accès.
mathk

Pour l'utiliser clazz2 instanceof clazz1correctement, vous pouvez utiliser à la place class2.isAssignableFrom(clazz1), ce qui, je crois, retournerait vrai dans votre exemple.
Simon Forsberg

14

La substitution de méthode est rendue possible par la répartition dynamique , ce qui signifie que le type déclaré d'un objet ne détermine pas son comportement, mais plutôt son type d'exécution:

Animal lassie = new Dog();
lassie.speak(); // outputs "woof!"
Animal kermit = new Frog();
kermit.speak(); // outputs "ribbit!"

Même si les deux lassieet kermitsont déclarés comme des objets de type Animal, leur comportement (méthode .speak()) varie car la répartition dynamique ne liera l'appel de méthode .speak()à une implémentation qu'au moment de l'exécution - pas au moment de la compilation.

Maintenant, voici où le staticmot - clé commence à avoir un sens: le mot "statique" est un antonyme pour "dynamique". Donc, la raison pour laquelle vous ne pouvez pas remplacer les méthodes statiques est qu'il n'y a pas de répartition dynamique sur les membres statiques - parce que statique signifie littéralement "non dynamique". S'ils distribuaient dynamiquement (et pouvaient donc être remplacés), le staticmot clé n'aurait tout simplement plus de sens.


11

Oui. Pratiquement Java permet de remplacer la méthode statique, et non théoriquement si vous remplacez une méthode statique en Java, elle se compilera et s'exécutera sans problème, mais elle perdra le polymorphisme qui est la propriété de base de Java. Vous lirez partout qu'il n'est pas possible d'essayer de compiler et d'exécuter vous-même. vous obtiendrez votre réponse. Par exemple, si vous avez Class Animal et une méthode statique, eat () et que vous remplacez cette méthode statique dans sa sous-classe, appelons-la Dog. Ensuite, lorsque vous affectez un objet Chien à une référence animale et que vous appelez eat () selon Java Dog, eat () aurait dû être appelé mais en mode statique Overriding Animals, eat () sera appelé.

class Animal {
    public static void eat() {
        System.out.println("Animal Eating");
    }
}

class Dog extends Animal{
    public static void eat() {
        System.out.println("Dog Eating");
    }
}

class Test {
    public static void main(String args[]) {
       Animal obj= new Dog();//Dog object in animal
       obj.eat(); //should call dog's eat but it didn't
    }
}


Output Animal Eating

Selon le principe de polymorphisme de Java, la sortie devrait être Dog Eating.
Mais le résultat était différent car pour prendre en charge le polymorphisme, Java utilise la liaison tardive, ce qui signifie que les méthodes ne sont appelées qu'au moment de l'exécution, mais pas dans le cas des méthodes statiques. Dans le compilateur de méthodes statiques, le compilateur appelle des méthodes au moment de la compilation plutôt qu'au moment de l'exécution, nous obtenons donc des méthodes en fonction de la référence et non en fonction de l'objet une référence contenant ce qui explique pourquoi vous pouvez dire Pratiquement, il prend en charge le dépassement statique, mais théoriquement, il ne fonctionne pas 't.


3
C'est une mauvaise pratique d'appeler des méthodes statiques à partir d'un objet.
Dmitry Zagorulkin

6

la substitution est réservée aux membres d'instance pour prendre en charge le comportement polymorphe. les membres de classe statiques n'appartiennent pas à une instance particulière. au lieu de cela, les membres statiques appartiennent à la classe et, par conséquent, la substitution n'est pas prise en charge car les sous-classes héritent uniquement des membres d'instance protégés et publics et non des membres statiques. Vous voudrez peut-être définir une usine d'interface et de recherche et / ou des modèles de conception de stratégie pour évaluer une autre approche.


1
N'avez-vous lu aucune des autres réponses qui couvrent déjà cela et indiquent clairement que ce ne sont pas des raisons suffisantes pour ignorer les statistiques dominantes au niveau conceptuel. Nous savons que cela ne fonctionne pas. Il est parfaitement "propre de désirer le dépassement des méthodes statiques et en effet c'est possible dans de nombreuses autres langues.
RichieHH

Richard, supposons un instant que lorsque j'ai répondu à cette question il y a 4 ans, la plupart de ces réponses n'avaient pas été publiées, alors ne soyez pas un con! Il n'est pas nécessaire d'affirmer que je n'ai pas lu attentivement. De plus, n'avez-vous pas lu que nous ne discutons que de la priorité par rapport à Java. Qui se soucie de ce qui est possible dans d'autres langues. Ce n'est pas pertinent. Allez troll ailleurs. Votre commentaire n'ajoute rien de valeur à ce fil.
Athens Holloway

6

En Java (et dans de nombreux langages OOP, mais je ne peux pas parler pour tous; et certains n'ont pas du tout statique) toutes les méthodes ont une signature fixe - les paramètres et les types. Dans une méthode virtuelle, le premier paramètre est implicite: une référence à l'objet lui-même et lorsqu'il est appelé à partir de l'objet, le compilateur ajoute automatiquement this.

Il n'y a pas de différence pour les méthodes statiques - elles ont toujours une signature fixe. Cependant, en déclarant la méthode statique, vous avez explicitement déclaré que le compilateur ne doit pas inclure le paramètre d'objet implicite au début de cette signature. Par conséquent, tout autre code qui appelle cela ne doit pas tenter de mettre une référence à un objet sur la pile . Si c'était le cas, l'exécution de la méthode ne fonctionnerait pas car les paramètres seraient au mauvais endroit - décalés d'un - sur la pile.

En raison de cette différence entre les deux; les méthodes virtuelles ont toujours une référence à l'objet contextuel (c'est-à-dire this), il est donc possible de référencer tout ce qui, dans le tas, appartient à cette instance de l'objet. Mais avec les méthodes statiques, étant donné qu'aucune référence n'est transmise, cette méthode ne peut accéder à aucune variable et méthode d'objet car le contexte n'est pas connu.

Si vous souhaitez que Java modifie la définition afin qu'un contexte d'objet soit transmis pour chaque méthode, statique ou virtuelle, alors vous ne disposerez essentiellement que de méthodes virtuelles.

Comme quelqu'un l'a demandé dans un commentaire à l'op - quelle est la raison et le but de cette fonctionnalité?

Je ne connais pas beaucoup Ruby, comme cela a été mentionné par le PO, j'ai fait quelques recherches. Je vois que dans Ruby, les classes sont vraiment un type spécial d'objet et on peut créer (même dynamiquement) de nouvelles méthodes. Les classes sont des objets de classe complète en Ruby, ils ne sont pas en Java. C'est juste quelque chose que vous devrez accepter lorsque vous travaillerez avec Java (ou C #). Ce ne sont pas des langages dynamiques, bien que C # ajoute certaines formes de dynamique. En réalité, Ruby n'a pas de méthodes "statiques" pour autant que je puisse trouver - dans ce cas, ce sont des méthodes sur l'objet de classe singleton. Vous pouvez ensuite remplacer ce singleton par une nouvelle classe et les méthodes de l'objet de classe précédent appellent celles définies dans la nouvelle classe (correct?). Donc, si vous appelez une méthode dans le contexte de la classe d'origine, elle n'exécutera toujours que la statique d'origine, mais appeler une méthode dans la classe dérivée, appelerait des méthodes à partir du parent ou de la sous-classe. Intéressant et je peux y voir une certaine valeur. Il faut un schéma de pensée différent.

Puisque vous travaillez en Java, vous devrez vous adapter à cette façon de faire. Pourquoi ont-ils fait ça? Eh bien, probablement pour améliorer les performances à l'époque en fonction de la technologie et de la compréhension disponibles. Les langages informatiques sont en constante évolution. Remontez assez loin et il n'y a rien de tel que la POO. À l'avenir, il y aura d'autres nouvelles idées.

EDIT : Un autre commentaire. Maintenant que je vois les différences et en tant que développeur Java / C # moi-même, je peux comprendre pourquoi les réponses que vous obtenez des développeurs Java peuvent prêter à confusion si vous venez d'un langage comme Ruby. Les staticméthodes Java ne sont pas les mêmes que les classméthodes Ruby . Les développeurs Java auront du mal à comprendre cela, tout comme ceux qui travaillent principalement avec un langage comme Ruby / Smalltalk. Je peux voir comment cela serait également très déroutant par le fait que Java utilise également la "méthode de classe" comme une autre façon de parler des méthodes statiques, mais ce même terme est utilisé différemment par Ruby. Java n'a pas de méthodes de classe de style Ruby (désolé); Ruby ne dispose pas de méthodes statiques de style Java qui ne sont en fait que de vieilles fonctions de style procédural, comme en C.

Soit dit en passant - merci pour la question! J'ai appris quelque chose de nouveau pour moi aujourd'hui sur les méthodes de classe (style Ruby).


1
Bien sûr, il n'y a aucune raison pour que Java ne puisse pas passer un objet "Class" en tant que paramètre caché aux méthodes statiques. Il n'était tout simplement pas conçu pour le faire.
jmucchiello

6

Eh bien ... la réponse est NON si vous pensez du point de vue du comportement d'une méthode remplacée en Java. Mais, vous n'obtenez aucune erreur du compilateur si vous essayez de remplacer une méthode statique. Cela signifie que si vous essayez de remplacer, Java ne vous empêche pas de le faire; mais vous n'obtiendrez certainement pas le même effet que vous obtenez pour les méthodes non statiques. Le remplacement en Java signifie simplement que la méthode particulière serait appelée en fonction du type d'exécution de l'objet et non du type de compilation de celui-ci (ce qui est le cas avec les méthodes statiques remplacées). D'accord ... des suppositions pour la raison pour laquelle se comportent-ils étrangement? Parce que ce sont des méthodes de classe et que l'accès à celles-ci est toujours résolu pendant la compilation uniquement en utilisant les informations de type de compilation.

Exemple : essayons de voir ce qui se passe si nous essayons de remplacer une méthode statique: -

class SuperClass {
// ......
public static void staticMethod() {
    System.out.println("SuperClass: inside staticMethod");
}
// ......
}

public class SubClass extends SuperClass {
// ......
// overriding the static method
public static void staticMethod() {
    System.out.println("SubClass: inside staticMethod");
}

// ......
public static void main(String[] args) {
    // ......
    SuperClass superClassWithSuperCons = new SuperClass();
    SuperClass superClassWithSubCons = new SubClass();
    SubClass subClassWithSubCons = new SubClass();

    superClassWithSuperCons.staticMethod();
    superClassWithSubCons.staticMethod();
    subClassWithSubCons.staticMethod();
    // ...
}
}

Sortie : -
SuperClass: inside staticMethod
SuperClass: inside staticMethod
SubClass: inside staticMethod

Remarquez la deuxième ligne de la sortie. Si le staticMethod avait été remplacé, cette ligne aurait dû être identique à la troisième ligne car nous invoquons le 'staticMethod ()' sur un objet de type Runtime comme 'SubClass' et non pas comme 'SuperClass'. Cela confirme que les méthodes statiques sont toujours résolues en utilisant uniquement leurs informations de type de temps de compilation.


5

En général, cela n'a pas de sens de permettre le «remplacement» des méthodes statiques car il n'y aurait pas de bon moyen de déterminer laquelle appeler au moment de l'exécution. Prenons l'exemple de l'employé, si nous appelons RegularEmployee.getBonusMultiplier () - quelle méthode est censée être exécutée?

Dans le cas de Java, on pourrait imaginer une définition de langage où il est possible de "remplacer" les méthodes statiques tant qu'elles sont appelées via une instance d'objet. Cependant, tout ce que cela ferait serait de réimplémenter des méthodes de classe régulières, en ajoutant de la redondance au langage sans vraiment ajouter aucun avantage.


2
Intuitivement, je pense que cela devrait fonctionner comme une fonction virtuelle. Si B étend A et A et B ont des fonctions virtuelles nommées doStuff, le compilateur sait que les instances de A doivent utiliser A.doStuff et les instances de B doivent utiliser B.doStuff. Pourquoi ne peut-il pas faire de même avec les fonctions statiques? Après tout, le compilateur sait de quelle classe chaque objet est une instance.
Jay

Euh ... Jay, une méthode statique n'a pas besoin (généralement n'est pas) appelée sur une instance ...
meriton

2
@meriton, mais c'est encore plus facile, non? Si une méthode statique est appelée à l'aide d'un nom de classe, vous utiliseriez la méthode appropriée pour la classe.
CPerkins

Mais alors, qu'est-ce qui est primordial pour vous? Si vous appelez A.doStuff () s'il utilise la version qui a été remplacée dans «B étend A» ou la version qui a été remplacée dans «C étend A». Et si vous avez C ou B, vous appelez ces versions de toute façon ... aucune substitution nécessaire.
PSpeed

@meriton: Il est vrai qu'une méthode statique n'est généralement pas appelée avec une instance, mais je suppose que c'est parce qu'un tel appel ne fait rien d'utile étant donné la conception actuelle de Java! Je suggère qu'une conception alternative aurait pu être une meilleure idée. BTW, dans un sens très réel, les fonctions statiques sont appelées avec une instance assez régulièrement: lorsque vous appelez la fonction statique à partir d'une fonction virtuelle. Ensuite, vous obtenez implicitement this.function (), c'est-à-dire l'instance actuelle.
Jay

5

En remplaçant, nous pouvons créer une nature polymorphe en fonction du type d'objet. La méthode statique n'a aucun rapport avec l'objet. Ainsi, java ne peut pas prendre en charge la substitution de méthode statique.


5

J'aime et double le commentaire de Jay ( https://stackoverflow.com/a/2223803/1517187 ).
Je suis d'accord que c'est la mauvaise conception de Java.
De nombreuses autres langues prennent en charge la substitution des méthodes statiques, comme nous le voyons dans les commentaires précédents. Je pense que Jay est également venu à Java de Delphi comme moi.
Delphi (Object Pascal) a été le premier langage à implémenter la POO.
Il est évident que beaucoup de gens avaient de l'expérience avec cette langue car c'était dans le passé la seule langue pour écrire des produits GUI commerciaux. Et - oui, nous pourrions dans Delphi remplacer les méthodes statiques. En fait, les méthodes statiques dans Delphi sont appelées "méthodes de classe", tandis que Delphi avait le concept différent de "méthodes statiques Delphi" qui étaient des méthodes avec liaison anticipée. Pour outrepasser les méthodes, vous avez dû utiliser une liaison tardive, déclarez la directive "virtuelle". C'était donc très pratique et intuitif et je m'attendrais à cela en Java.


3

À quoi cela sert-il de remplacer les méthodes statiques? Vous ne pouvez pas appeler de méthodes statiques via une instance.

MyClass.static1()
MySubClass.static1()   // If you overrode, you have to call it through MySubClass anyway.

EDIT: Il semble que par une mauvaise surveillance de la conception du langage, vous pouvez appeler des méthodes statiques via une instance. En général, personne ne fait ça. Ma faute.


8
"Vous ne pouvez pas appeler des méthodes statiques via une instance" En fait, l'une des bizarreries de Java est que vous POUVEZ appeler des méthodes statiques via une instance, même si c'est une très mauvaise idée.
Powerlord

1
En effet, Java permet d'accéder aux membres statiques via des instances: voir Variable statique en Java
Richard JP Le Guen

Un IDE moderne approprié générera un avertissement quand vous le ferez, donc au moins vous pouvez l'attraper tandis qu'Oracle peut continuer la compatibilité ascendante.
Gimby

1
Il n'y a rien de mal sur le plan conceptuel à pouvoir appeler une méthode statique via une instance. C'est chicaner pour le plaisir de chicaner. Pourquoi quelque chose comme une instance de Date NE devrait PAS appeler ses propres méthodes statiques en passant des données d'instance à la fonction via l'interface d'appel?
RichieHH

@RichieHH Ce n'est pas de cela qu'ils chicanent. La question est de savoir pourquoi est-il autorisé d'appeler variable.staticMethod(), au lieu de Class.staticMethod(), où variableest une variable avec un type déclaré Class. Je suis d'accord que c'est un mauvais langage.
fishinear

3

La substitution en Java signifie simplement que la méthode particulière serait appelée en fonction du type d'exécution de l'objet et non en fonction de son type au moment de la compilation (ce qui est le cas avec les méthodes statiques substituées). Comme les méthodes statiques sont des méthodes de classe, elles ne sont pas des méthodes d'instance, elles n'ont donc rien à voir avec le fait que la référence pointe vers quel objet ou instance, car en raison de la nature de la méthode statique, elle appartient à une classe spécifique. Vous pouvez la redéclarer dans la sous-classe, mais cette sous-classe ne saura rien des méthodes statiques de la classe parente car, comme je l'ai dit, elle est spécifique uniquement à la classe dans laquelle elle a été déclarée. Les accéder à l'aide de références d'objets n'est qu'une liberté supplémentaire donnée par les concepteurs de Java et nous ne devons certainement pas penser à arrêter cette pratique uniquement lorsqu'ils la restreignent plus de détails et d'exemples. http://faisalbhagat.blogspot.com/2014/09/method-overriding-and-method-hiding.html


3

En remplaçant, vous obtenez un polymorphisme dynamique. Lorsque vous dites remplacer les méthodes statiques, les mots que vous essayez d'utiliser sont contradictoires.

Static dit - temps de compilation, le remplacement est utilisé pour le polymorphisme dynamique. Les deux sont de nature opposée et ne peuvent donc pas être utilisés ensemble.

Le comportement polymorphe dynamique survient lorsqu'un programmeur utilise un objet et accède à une méthode d'instance. JRE mappera différentes méthodes d'instance de différentes classes en fonction du type d'objet que vous utilisez.

Lorsque vous dites remplacer les méthodes statiques, les méthodes statiques auxquelles nous accèderons en utilisant le nom de classe, qui sera lié au moment de la compilation, il n'y a donc pas de concept de liaison des méthodes à l'exécution avec des méthodes statiques. Ainsi, le terme «remplacer» les méthodes statiques lui-même n'a aucun sens.

Remarque: même si vous accédez à une méthode de classe avec un objet, le compilateur java est toujours assez intelligent pour le découvrir et fera une liaison statique.


Ce n'est pas vrai dans de nombreux cas où au moment de l'exécution, la méthode statique est en fait appelée à partir d'une instance de la classe contenante et il est donc parfaitement possible de déterminer les instances de la fonction à invoquer.
RichieHH

statique ne signifie pas le temps de compilation, statique signifie qu'il est lié à la classe plutôt qu'à tout objet spécifique. cela a autant de sens, sinon plus, que de créer une fabrique de classe, sauf que statique a Box.createBoxplus de sens que BoxFactory.createBox, et est un modèle inévitable lorsqu'il faut vérifier la construction sans lancer d'exceptions (les constructeurs ne peuvent pas échouer, ils ne peuvent que tuer le processus / throw exception), alors qu'une méthode statique peut retourner null en cas d'échec, ou même accepter des rappels de succès / erreur pour écrire quelque chose comme hastebin.com/codajahati.java .
Dmitry

2

La réponse à cette question est simple, la méthode ou la variable marquée comme statique appartient à la classe uniquement, de sorte que cette méthode statique ne peut pas être héritée dans la sous-classe car elle appartient à la super classe uniquement.


1
Salut G4uKu3_Gaurav. Merci d'avoir décidé de contribuer. Cependant, nous attendons généralement des réponses plus longues et plus détaillées que cela.
DJClayworth

@DJClayworth vous devez suivre ce lien pour une réponse détaillée geeksforgeeks.org/…
g1ji

Merci pour le lien. En fait, je suis ici pour aider les nouveaux arrivants sur le site, pour expliquer comment le site fonctionne pour ceux qui n'y sont pas habitués, pas parce que j'avais besoin d'une réponse à la question.
DJClayworth

1

Solution simple: utilisez une instance singleton. Il permettra les remplacements et l'héritage.

Dans mon système, j'ai la classe SingletonsRegistry, qui retourne une instance pour la classe passée. Si l'instance n'est pas trouvée, elle est créée.

Cours de langue Haxe:

package rflib.common.utils;
import haxe.ds.ObjectMap;



class SingletonsRegistry
{
  public static var instances:Map<Class<Dynamic>, Dynamic>;

  static function __init__()
  {
    StaticsInitializer.addCallback(SingletonsRegistry, function()
    {
      instances = null;
    });

  } 

  public static function getInstance(cls:Class<Dynamic>, ?args:Array<Dynamic>)
  {
    if (instances == null) {
      instances = untyped new ObjectMap<Dynamic, Dynamic>();      
    }

    if (!instances.exists(cls)) 
    {
      if (args == null) args = [];
      instances.set(cls, Type.createInstance(cls, args));
    }

    return instances.get(cls);
  }


  public static function validate(inst:Dynamic, cls:Class<Dynamic>)
  {
    if (instances == null) return;

    var inst2 = instances[cls];
    if (inst2 != null && inst != inst2) throw "Can\'t create multiple instances of " + Type.getClassName(cls) + " - it's singleton!";
  }

}

Très cool, c'est la première fois que j'entends parler du langage de programmation Haxe :)
cyc115

1
Ceci est beaucoup mieux implémenté en Java comme méthode statique sur la classe elle-même; Singleton.get(). Le registre est juste une surcharge, et il empêche GC sur les classes.
Lawrence Dol

Vous avez raison, c'est une solution classique. Je ne me souviens pas exactement pourquoi j'ai choisi le registre, j'avais probablement un cadre de pensée qui a conduit à ce résultat.
Raivo Fishmeister

1

Une méthode statique, une variable, un bloc ou une classe imbriquée appartient à la classe entière plutôt qu'à un objet.

Une méthode en Java est utilisée pour exposer le comportement d'un objet / classe. Ici, comme la méthode est statique (c.-à-d., La méthode statique est utilisée pour représenter le comportement d'une classe uniquement.) Changer / remplacer le comportement de toute la classe violera le phénomène de l'un des piliers fondamentaux de la programmation orientée objet, c'est-à-dire une cohésion élevée . (rappelez-vous qu'un constructeur est un type spécial de méthode en Java.)

Haute cohésion - Une classe ne devrait avoir qu'un seul rôle. Par exemple: une classe de voiture ne devrait produire que des objets de voiture et non des vélos, des camions, des avions, etc. Mais la classe de voiture peut avoir certaines caractéristiques (comportement) qui n'appartiennent qu'à elle-même.

Par conséquent, lors de la conception du langage de programmation Java. Les concepteurs de langage pensaient permettre aux développeurs de garder certains comportements d'une classe pour eux uniquement en rendant une méthode statique dans la nature.


Le code pièce ci-dessous essaie de remplacer la méthode statique, mais ne rencontrera aucune erreur de compilation.

public class Vehicle {
static int VIN;

public static int getVehileNumber() {
    return VIN;
}}

class Car extends Vehicle {
static int carNumber;

public static int getVehileNumber() {
    return carNumber;
}}

En effet, ici , nous ne surchargez pas une méthode , mais nous sommes juste re-déclarer il. Java permet la re-déclaration d'une méthode (statique / non statique).

La suppression du mot clé statique de la méthode getVehileNumber () de la classe Car entraînera une erreur de compilation. Depuis, nous essayons de modifier la fonctionnalité de la méthode statique qui appartient uniquement à la classe Vehicle.

De plus, si getVehileNumber () est déclaré final, le code ne sera pas compilé, puisque le mot clé final empêche le programmeur de déclarer à nouveau la méthode.

public static final int getVehileNumber() {
return VIN;     }

Dans l'ensemble, c'est aux concepteurs de logiciels de savoir où utiliser les méthodes statiques. Personnellement, je préfère utiliser des méthodes statiques pour effectuer certaines actions sans créer d'instance de classe. Deuxièmement, cacher le comportement d'une classe au monde extérieur.


1

Voici une explication simple. Une méthode statique est associée à une classe tandis qu'une méthode d'instance est associée à un objet particulier. Les remplacements permettent d'appeler les différentes implémentations des méthodes substituées associées à l'objet particulier. Il est donc contre-intuitif de remplacer la méthode statique qui n'est même pas associée aux objets mais à la classe elle-même en premier lieu. Les méthodes statiques ne peuvent donc pas être remplacées en fonction de l'objet qui l'appelle, elles seront toujours associées à la classe où elles ont été créées.


Comment est-il contre-intuitif d'avoir une public abstract IBox createBox();interface IBox interne? Box peut implémenter IBox pour remplacer createBox, et faire en sorte que la création d'objet aboutisse à un IBox valide, sinon retourner un null. Les constructeurs ne peuvent pas retourner "null" et donc vous êtes obligé (1) d'utiliser des exceptions PARTOUT (ce que nous faisons maintenant), ou (2) de créer des classes d'usine qui font ce que j'ai dit plus tôt mais d'une manière qui n'a aucun sens pour les novices ou les experts de Java (ce que nous faisons aussi maintenant). Les méthodes statiques non implémentées résolvent ce problème.
Dmitry

-1

Maintenant, en voyant les réponses ci-dessus, tout le monde sait que nous ne pouvons pas remplacer les méthodes statiques, mais il ne faut pas mal comprendre le concept d'accès aux méthodes statiques à partir de la sous-classe .

Nous pouvons accéder aux méthodes statiques de la super classe avec référence de sous-classe si cette méthode statique n'a pas été cachée par une nouvelle méthode statique définie dans la sous-classe.

Par exemple, voir ci-dessous le code: -

public class StaticMethodsHiding {
    public static void main(String[] args) {
        SubClass.hello();
    }
}


class SuperClass {
    static void hello(){
        System.out.println("SuperClass saying Hello");
    }
}


class SubClass extends SuperClass {
    // static void hello() {
    // System.out.println("SubClass Hello");
    // }
}

Production:-

SuperClass saying Hello

Consultez la documentation Java oracle et recherchez ce que vous pouvez faire dans une sous-classe pour plus de détails sur le masquage des méthodes statiques dans la sous-classe.

Merci


-3

Le code suivant montre que c'est possible:

class OverridenStaticMeth {   

static void printValue() {   
System.out.println("Overriden Meth");   
}   

}   

public class OverrideStaticMeth extends OverridenStaticMeth {   

static void printValue() {   
System.out.println("Overriding Meth");   
}   

public static void main(String[] args) {   
OverridenStaticMeth osm = new OverrideStaticMeth();   
osm.printValue();   

System.out.println("now, from main");
printValue();

}   

} 

1
Non, ce n'est pas le cas; le type statique déclaré de osmn'est OverridenStaticMethpas OverrideStaticMeth.
Lawrence Dol

2
Aussi, j'essaierais d'éviter d'utiliser autant de Meth lors de la programmation de <big grin>;
Lawrence Dol
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.