563 votes

Pourquoi n ' t Java permet la substitution de méthodes statiques ?

Pourquoi n’est-il pas possible de substituer les méthodes statiques ?

Si possible, veuillez utiliser un exemple.

512voto

Nathan Hughes Points 30377

Primordial dépend de l'existence d'une instance d'une classe. Le point de polymorphisme, c'est que vous pouvez sous-classe d'une classe et les objets de mise en œuvre de ces sous-classes auront des comportements différents pour les mêmes méthodes définies dans la classe mère (et remplacé dans les sous-classes). Une méthode statique n'est pas associée à une instance de classe de sorte que le concept n'est pas applicable.

Il y avait deux considérations relatives à la conduite de Java est la conception qui a touché cette. L'un a eu un souci avec la performance: il y a eu beaucoup de critiques de Smalltalk à ce sujet étant trop lent (collecte des ordures et polymorphe des appels en cours de partie) et de Java, les créateurs, ont été déterminées à l'éviter. Un autre a été la décision que le public cible de Java a été développeurs C++. Faire statiques les méthodes de travail de la façon de faire a l'avantage de la familiarité pour les programmeurs en C++ et a également été très rapide, car il n'y a pas besoin d'attendre la fin de l'exécution pour déterminer la méthode à appeler.

191voto

Jay Points 14781

Personnellement, je pense que c'est une faille dans la conception de Java. Oui, oui, je comprends que les non-méthodes statiques sont attachés à une instance alors que les méthodes statiques sont attachés à une classe, etc etc. Encore, considérons 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 vous en doutez. À savoir, SpecialEmployee obtenir un bonus de 2% tout comme les employés réguliers. Mais si vous retirez le "statique"s, puis SpecialEmployee obtenir un 3% de bonus.

(Certes, cet exemple est un mauvais codage de style en ce que dans la vraie vie, vous voudrez probablement le multiplicateur de bonus pour être dans une base de données quelque part plutôt que de codé en dur. Mais c'est juste parce que je ne voulais pas ralentir l'exemple avec beaucoup de code superflu pour le moment.)

Il semble tout à fait plausible pour moi que vous voudrez peut-être faire getBonusMultiplier statique. Peut-être que vous voulez être en mesure d'afficher le multiplicateur de bonus pour toutes les catégories de salariés, sans avoir besoin d'avoir une instance d'un employé dans chaque catégorie. Quel serait le point de la recherche pour un tel exemple instances? Que faire si la création d'une nouvelle catégorie de salariés et n'ont pas d'employés affectés à l'-il encore? C'est assez logiquement d'une fonction statique.

Mais ça ne fonctionne pas.

Et oui, oui, je crois qu'un certain nombre de façons de réécrire le code ci-dessus pour le faire fonctionner. Mon point n'est pas que cela crée un problème insoluble, mais qu'il crée un piège pour les imprudents programmeur, parce que la langue ne se comporte pas comme je pense qu'une personne raisonnable pourrait s'attendre.

Peut-être que si j'ai essayé d'écrire un compilateur pour un langage de programmation orientée objet, je voudrais rapidement voir pourquoi la mise en œuvre de sorte que les fonctions statiques peuvent être surchargée serait difficile, voire impossible.

Ou peut-être il ya quelques bonnes raisons de Java se comporte de cette manière. Quelqu'un peut-il point un avantage à ce problème, certaines catégorie de problème est rendu plus facile par cette? Je veux dire, ne pas me pointer à la Java langage de spécification et dire: "voyez, c'est documenté comment il se comporte". Je sais qu'. Mais est-il une bonne raison pourquoi il DEVRAIT se comporter de cette façon? (En plus de l'évident "mettre en œuvre le droit était trop dur"...)

Mise à jour

@VicKirk: Si vous voulez dire que c'est "une mauvaise conception" parce que ça ne correspond pas à la façon dont Java gère la statique, ma réponse est "non, sans blague, bien sûr." Comme je l'ai dit dans mon premier post, il ne fonctionne pas. Mais si vous voulez dire que c'est mauvais de conception dans le sens où il y aurait quelque chose de fondamentalement mauvais dans une langue où il a travaillé, c'est à dire où la statique pourrait être remplacée comme des fonctions virtuelles, que ce serait en quelque sorte introduire une ambiguïté ou il serait impossible de mettre en œuvre de manière efficace ou quelque chose comme ça, je réponds, "Pourquoi? Quel est le problème avec le concept?"

Je pense que l'exemple que je donne est tout à fait naturel de vouloir faire des. J'ai une classe qui a une fonction qui ne dépend pas de toutes les données de l'instance, et que je pourrais très raisonnable voulez l'appeler indépendant de l'instance, ainsi que de vouloir appeler à partir d'une méthode d'instance. Pourquoi faudrait-il pas travailler? J'ai couru dans cette situation, un bon nombre de fois au fil des ans. Dans la pratique, je le contourner en faisant de 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 fictive. Cela semble être une très rond-point pour y arriver.

45voto

Steve Powell Points 4722

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

Voici un bout de code qui illustre l' état actuel des choses en 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 ce code (je l'ai fait sur un Mac, à partir de l'Éclipse, à l'aide de la version 1.6 de Java), 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, le seul cas qui pourrait être une surprise (et à qui la question est à propos) semble être le premier cas:

"Le type à l'exécution n'est pas utilisée pour déterminer les méthodes statiques sont appelés, même en cas d'appel d'une instance d'objet (obj.staticMethod())."

et le dernier cas:

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

L'appel à une instance d'objet

L'appel statique est résolu à la compilation, alors qu'un non-statique appel de la méthode est résolu au moment de l'exécution. Notez que bien que les méthodes statiques sont héritées (du parent), ils ne sont pas remplacées (par enfant). Cela pourrait être une surprise si vous avez prévu autrement.

L'appel à partir d'une méthode de l'objet

Objet les appels de méthode sont résolus en utilisant le type à l'exécution, mais statique (classe) les appels de méthode sont résolus à l'aide de la compilation (déclaré) de type.

Changer les règles

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

Ce serait facilement faisable (si nous l'avons changé en Java :-O), et n'est pas du tout déraisonnable, cependant, il a fait d'intéressantes considérations.

La considération principale est que nous avons besoin de décider qui des appels aux méthodes statiques devrait le faire.

Pour le moment, Java a cette "bizarrerie" dans la langue, dans laquelle obj.staticMethod() des appels sont remplacés par ObjectClass.staticMethod() des appels (normalement avec un avertissement). [Note: ObjectClass est le type de compilation d' obj.] Celles-ci seraient de bons candidats pour remplacer de cette manière, en prenant le type à l'exécution de l' obj.

Si on le faisait, il serait de rendre le corps de méthode plus difficile à lire: les appels statiques dans une classe parent, pourrait éventuellement être dynamiquement "re-routé". Pour éviter cela, nous devons appeler la méthode statique avec un nom de classe -- et de ce fait, les appels de plus en plus évidents résolu avec le type de compilation de la hiérarchie (comme aujourd'hui).

Les autres moyens de l'invocation d'une méthode statique, qui sont plus compliqués: this.staticMethod() devrait signifier la même chose que obj.staticMethod(), en prenant le type à l'exécution de l' this. Cependant, cela peut provoquer quelques maux de tête avec des programmes existants, qui appellent (apparemment local) méthodes statiques sans décoration (ce qui est sans doute l'équivalent de this.method()).

Alors, sans fioritures appels staticMethod()? Je suggère de faire la même chose qu'aujourd'hui, et utiliser le local de classe de contexte pour décider quoi faire. Sinon, une grande confusion s'ensuivrait. Bien sûr, cela signifie qu' method() signifierait this.method() si method non méthode statique, et ThisClass.method() si method ont une méthode statique. C'est une autre source de confusion.

D'autres considérations

Si nous avons modifié ce comportement (et de fait les appels statiques potentiellement de manière dynamique non-local), nous aurions probablement envie de revisiter le sens de l' final, private et protected comme qualificatifs sur static les méthodes d'une classe. Nous serions alors tous ont du s'habituer au fait qu' private static et public final méthodes ne sont pas remplacées, et peut donc être résolu de manière satisfaisante au moment de la compilation, et sont "safe" pour lire comme des références locales.

25voto

Patlatus Points 226

En fait nous avons eu tort.
En dépit de Java ne permet pas de remplacer les méthodes statiques par défaut, si vous regardez à fond dans la documentation de la Classe et de la Méthode des classes en Java, vous pouvez toujours trouver une façon d'imiter les méthodes statiques primordial par 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());
    }
}

Résultat:

0.02
0.02
0.02
0.03

ce que nous essayons de réaliser :)

Même si nous déclarons troisième variable Carl comme RegularEmployee et de l'attribuer à l'instance de SpecialEmployee, nous aurons encore l'appel de RegularEmployee méthode dans le premier cas, et à l'appel du SpecialEmployee méthode dans le second cas

RegularEmployee Carl = new SpecialEmployee();

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

il suffit de regarder la sortie de la console:

0.02
0.03

;)

19voto

ewernli Points 23180

Les méthodes statiques sont traités comme des mondiaux par la JVM, il ne sont pas liés à une instance d'objet.

Il pourrait théoriquement être possible que si l'on pourrait appeler des méthodes statiques de la classe d'objets (comme dans les langages comme Smalltalk), mais ce n'est pas le cas en Java.

MODIFIER

Vous pouvez surcharge de la méthode statique, c'est ok. Mais vous ne pouvez pas remplacer une méthode statique, car la classe ne sont de première classe de l'objet. 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 n'est pas parallèle à la hiérarchie de classe.

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 ça s'arrête là. Vous n'avez pas appeler une méthode statique en utilisant clazz1.staticMethod(), mais à l'aide de MyClass.staticMethod(). Une méthode statique n'est pas liée à un objet et il n'y a donc pas de notion d' this ni super dans une méthode statique. Une méthode statique est une fonction globale; en conséquence, il ya aussi pas de notion de polymorphisme et, par conséquent, la redéfinition de méthode n'a pas de sens.

Mais cela pourrait être possible que si l' MyClass était un objet au moment de l'exécution à laquelle vous appelez une méthode, comme en Smalltalk (ou peut-être JRuby comme l'un des commentaires le suggèrent, mais je ne sais rien de JRuby).

Ah oui... une chose de plus. Vous pouvez appeler une méthode statique par l'intermédiaire d'un objet obj1.staticMethod() , mais qui a vraiment sucre syntaxique pour MyClass.staticMethod() et devrait être évitée. Habituellement, il soulève un avertissement dans les IDE modernes. Je ne sais pas pourquoi ils ont jamais permis à ce raccourci.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X