Pourquoi l'appel d'une méthode statique via une instance n'est-il pas une erreur pour le compilateur Java?

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

  •  03-07-2019
  •  | 
  •  

Question

Je suis sûr que vous connaissez tous le comportement que je veux dire - code tel que:

Thread thread = new Thread();
int activeCount = thread.activeCount();

provoque un avertissement du compilateur. Pourquoi n'est-ce pas une erreur?

EDIT:

Pour être clair: la question n'a rien à voir avec les threads. Je me rends compte que des exemples de threads sont souvent donnés lors de la discussion à cause de la possibilité de vraiment gâcher les choses avec eux. Mais le problème, c’est que cette utilisation est toujours un non-sens et que vous ne pouvez pas (en toute connaissance de cause) écrire un tel appel sans le vouloir. N'importe quel exemple de ce type d'appel de méthode serait banal. En voici un autre:

String hello = "hello";
String number123AsString = hello.valueOf(123);

Cela donne l’impression que chaque instance de String est livrée avec un " String valueOf (int i) " méthode.

Était-ce utile?

La solution

En gros, je pense que les concepteurs de Java ont commis une erreur lorsqu’ils ont conçu le langage. Il est trop tard pour le corriger en raison de problèmes de compatibilité. Oui, cela peut conduire à un code très trompeur. Oui, vous devriez l'éviter. Oui, vous devez vous assurer que votre IDE est configuré pour le traiter comme une erreur, IMO. Si vous concevez vous-même une langue, gardez-la à l’esprit comme exemple de ce qu’il faut éviter:)

Pour répondre à la remarque de DJClayworth, voici ce qui est autorisé en C #:

public class Foo
{
    public static void Bar()
    {
    }
}

public class Abc
{
    public void Test()
    {
        // Static methods in the same class and base classes
        // (and outer classes) are available, with no
        // qualification
        Def();

        // Static methods in other classes are available via
        // the class name
        Foo.Bar();

        Abc abc = new Abc();

        // This would *not* be legal. It being legal has no benefit,
        // and just allows misleading code
        // abc.Def();
    }

    public static void Def()
    {
    }
}

Pourquoi je pense que c'est trompeur? Parce que si je regarde le code someVariable.SomeMethod () , je pense qu'il utilisera la valeur de someVariable . Si SomeMethod () est une méthode statique, cette attente n'est pas valide. le code me trompe. Comment cela peut-il être une bonne chose?

Bizarrement, Java ne vous laissera pas utiliser une variable potentiellement non initialisée pour appeler une méthode statique, malgré le fait que la seule information à utiliser est le type déclaré de la variable. C'est un gâchis incohérent et inutile. Pourquoi l'autoriser?

EDIT: cette modification est une réponse à la réponse de Clayton, qui déclare autoriser l’héritage pour les méthodes statiques. Ce n'est pas. Les méthodes statiques ne sont tout simplement pas polymorphes. Voici un programme court mais complet pour démontrer que:

class Base
{
    static void foo()
    {
        System.out.println("Base.foo()");
    }
}

class Derived extends Base
{
    static void foo()
    {
        System.out.println("Derived.foo()");
    }
}

public class Test
{
    public static void main(String[] args)
    {
        Base b = new Derived();
        b.foo(); // Prints "Base.foo()"
        b = null;
        b.foo(); // Still prints "Base.foo()"
    }
}

Comme vous pouvez le constater, la valeur de b au moment de l'exécution est complètement ignorée.

Autres conseils

Pourquoi cela devrait-il être une erreur? L'instance a accès à toutes les méthodes statiques. Les méthodes statiques ne peuvent pas changer l'état de l'instance (essayer de est une erreur de compilation).

Le problème avec l'exemple bien connu que vous donnez est très spécifique aux threads , pas aux appels de méthodes statiques. Il semble que vous obteniez le activeCount () pour le thread référencé par le thread , mais vous obtenez réellement le compte du thread appelant. C’est une erreur logique que vous commettez en tant que programmeur. Émettre un avertissement est la bonne chose à faire pour le compilateur dans ce cas. C'est à vous de tenir compte de l'avertissement et de corriger votre code.

EDIT: Je me rends compte que la syntaxe du langage est ce qui vous permet d'écrire du code trompeur, mais souvenez-vous que le compilateur et ses avertissements font également partie du langage. Le langage vous permet de faire quelque chose que le compilateur juge douteux, mais il vous avertit que vous êtes conscient du risque de problèmes.

Ils ne peuvent plus en faire une erreur, à cause de tout le code qui existe déjà.

Je suis avec vous sur le fait que cela devrait être une erreur. Peut-être qu’il devrait y avoir une option / profil pour que le compilateur mette à niveau certains avertissements en erreurs.

Mise à jour: lorsqu'ils ont introduit le mot clé assert dans 1.4, qui présente des problèmes de compatibilité potentiels similaires avec l'ancien code, ils l'ont créé disponible uniquement si vous définissez explicitement le mode source sur" 1.4 ". Je suppose que l’on pourrait faire une erreur dans un nouveau mode source "java 7". Mais je doute qu'ils le fassent, considérant que tous les tracas que cela causerait. Comme d'autres l'ont fait remarquer, il n'est pas strictement nécessaire de vous empêcher d'écrire du code déroutant. Et les changements de langage apportés à Java devraient être limités au strict nécessaire à ce stade.

Réponse courte - la langue le permet, donc ce n'est pas une erreur.

Probablement pour le même logique que cela ne soit pas une erreur:

public class X
{
    public static void foo()
    {
    }

    public void bar()
    {
        foo(); // no need to do X.foo();
    }
}

Du point de vue du compilateur, l’important est qu’il soit capable de résoudre les symboles. Dans le cas d'une méthode statique, elle doit savoir dans quelle classe la rechercher, puisqu'elle n'est pas associée à un objet en particulier. Les concepteurs de Java ont évidemment décidé que, puisqu'ils pouvaient déterminer la classe d'un objet, ils pouvaient également résoudre la classe de toute méthode statique pour cet objet à partir de toute instance de l'objet. Ils choisissent de permettre cela - sous l'influence, peut-être, de l'observation de @ TofuBeer - pour donner une certaine commodité au programmeur. Les autres concepteurs de langues ont fait des choix différents. Je serais probablement tombé dans ce dernier camp, mais ce n'est pas un gros problème pour moi. J'autoriserais probablement l'utilisation mentionnée par @TofuBeer, mais le fait de ne pas autoriser l'accès à partir d'une variable d'instance est moins tenable.

Ce n'est pas une erreur car cela fait partie de la spécification, mais vous vous posez évidemment des questions sur les raisons, que nous pouvons tous deviner.

Je suppose que la source de ceci est en fait d'autoriser une méthode dans une classe à invoquer une méthode statique dans la même classe sans tracas. Comme appeler x () est légal (même sans le nom de classe self), appeler this.x () devrait également l'être, et donc appeler via n'importe quel objet était également légal.

Cela encourage également les utilisateurs à transformer les fonctions privées en fonctions statiques s’ils ne modifient pas l’état.

De plus, les compilateurs essaient généralement d’éviter de déclarer des erreurs s’il n’y avait aucun moyen que cela puisse conduire à une erreur directe. Puisqu’une méthode statique ne change pas l’état et ne prend pas soin de l’objet appelant, elle ne provoque pas d’erreur réelle (juste de la confusion). Un avertissement suffit.

La référence à la variable d'instance a pour seul but de fournir le type qui entoure la valeur statique. Si vous examinez le code d'octet appelant un statique via instance.staticMethod ou EnclosingClass.staticMethod produit le même bytecode d'invocation de méthode statique. Aucune référence à l'instance n'apparaît.

La réponse est aussi que c'est la raison pour laquelle elle est dans la liste. Tant que vous utilisez la classe. et non par le biais d'une instance, vous éviterez toute confusion à l'avenir.

Vous pouvez probablement le modifier dans votre environnement de développement (dans les préférences Eclipse - > Java - > Compilateur - > Erreurs / Avertissements)

Il n'y a pas d'option pour cela. En java (comme beaucoup d'autres lang.), Vous pouvez avoir accès à tous les membres statiques d'une classe via son nom de classe ou son objet d'instance de cette classe. C’est à vous et à votre cas et à votre solution logicielle de vous donner plus de lisibilité.

Je considère simplement ceci:

instanceVar.staticMethod();

être un raccourci pour cela:

instanceVar.getClass().staticMethod();

Si vous deviez toujours le faire:

SomeClass.staticMethod();

alors vous ne seriez pas capable de tirer parti de l'héritage pour les méthodes statiques.

C'est-à-dire qu'en appelant la méthode statique via l'instance, vous n'avez pas besoin de connaître la classe concrète de l'instance au moment de la compilation, mais seulement de mettre en œuvre staticMethod () quelque part dans la chaîne d'héritage.

EDIT: Cette réponse est fausse. Voir les commentaires pour plus de détails.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top