web-dev-qa-db-fra.com

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

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

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

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

MODIFIER:

Pour être clair: la question n'a rien à voir avec les discussions. 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 vraiment le problème est qu'une telle utilisation est toujours un non-sens et vous ne pouvez pas (avec compétence) écrire un tel appel et le signifier. Tout exemple de ce type d'appel de méthode serait barmy. En voici une autre:

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

Ce qui donne l'impression que chaque instance de String est livrée avec une méthode "String valueOf (int i)".

75
tmtest

Fondamentalement, je pense que les concepteurs de Java Java ont fait une erreur lorsqu'ils ont conçu le langage, et il est trop tard pour le corriger en raison des problèmes de compatibilité impliqués. Oui, cela peut conduire à un code très trompeur. Oui, vous devez l’éviter. Oui, vous devez vous assurer que votre IDE est configuré pour le traiter comme une erreur, OMI. Si vous concevez un langage vous-même, gardez-le à l’esprit comme exemple de la genre de chose à éviter :)

Juste pour répondre au point 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 est-ce que je pense que c'est trompeur? Parce que si je regarde le code someVariable.SomeMethod() je m'attends à ce qu'il tilise 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 chose bonne?

Bizarrement, Java ne vous permettra pas d'utiliser une variable potentiellement non initialisée pour appeler une méthode statique, malgré le fait que la seule information qu'il va utiliser est le type déclaré de la variable. C'est un désordre incohérent et inutile. Pourquoi le permettre?

EDIT: Cette édition est une réponse à la réponse de Clayton, qui prétend autoriser l'héritage pour les méthodes statiques. Ce n'est pas le cas. 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 voir, la valeur au moment de l'exécution de b est complètement ignorée.

78
Jon Skeet

Pourquoi serait-ce 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 (en essayant de est une erreur de compilation).

Le problème avec l'exemple bien connu que vous donnez est très spécifique à threads, pas aux appels de méthode statiques. Il semble que vous obteniez la activeCount() pour le thread référencé par thread, mais vous obtenez vraiment le nombre pour le thread appelant. Il s'agit d'une erreur logique que vous, en tant que programmeur, commettez. Émettre un avertissement est la chose appropriée que le compilateur doit faire 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 permettant d'écrire du code trompeur, mais rappelez-vous que le compilateur et ses avertissements font également partie du langage. Le langage vous permet de faire quelque chose que le compilateur considère comme douteux, mais il vous donne l'avertissement pour vous assurer que vous êtes conscient qu'il pourrait causer des problèmes.

13
Bill the Lizard

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 ce devrait être une erreur. Peut-être qu'il devrait y avoir une option/un profil pour le compilateur pour mettre à niveau certains avertissements en erreurs.

Mise à jour: Quand ils ont introduit le mot-clé assert dans 1.4, qui a des problèmes de compatibilité potentiels similaires avec l'ancien code, ils ont fait it disponible uniquement si vous définissez explicitement le mode source sur "1.4" . Je suppose que l'on pourrait en faire une erreur dans un nouveau mode source "Java 7". Mais je doute qu'ils le fassent, étant donné tous les tracas que cela entraînerait. Comme d'autres l'ont souligné, il n'est pas strictement nécessaire de vous empêcher d'écrire du code déroutant. Et les changements de langue à Java devrait être limité au strict nécessaire à ce stade.

9
Thilo

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

6
PaulJWilliams

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

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

Cela permet également d'encourager les utilisateurs à transformer des fonctions privées en statique s'ils ne changent pas l'état.

De plus, les compilateurs essaient généralement d'éviter de déclarer des erreurs lorsqu'il n'y a aucun moyen que cela puisse conduire à une erreur directe. Puisqu'une méthode statique ne change pas l'état ou ne se soucie pas de l'objet appelant, elle ne provoque pas une erreur réelle (juste une confusion) pour permettre cela. Un avertissement suffit.

2
Uri

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

La réponse est aussi pourquoi c'est là-dedans, eh bien c'est juste. Tant que vous utilisez la classe. et non via une instance, vous contribuerez à éviter toute confusion à l'avenir.

2
mP.

Probablement pour la même logique qui fait que ce n'est pas une erreur:

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

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

Du point de vue du compilateur, la chose vraiment importante est qu'il puisse résoudre les symboles. Dans le cas d'une méthode statique, elle doit savoir dans quelle classe la rechercher, car elle n'est associée à aucun objet particulier. Les concepteurs de Java ont évidemment décidé qu'étant donné qu'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 n'importe quelle instance de l'objet. Ils choisissent de permettre cela - influencé, peut-être, par l'observation de @ TofuBeer - pour donner au programmeur une certaine commodité. D'autres concepteurs de langues ont fait des choix différents. Je serais probablement tombé dans ce dernier camp, mais ce n'est pas si grave pour moi. J'autoriserais probablement l'utilisation mentionnée par @TofuBeer, mais l'ayant autorisée, ma position sur l'interdiction d'accès à partir d'une variable d'instance est moins tenable.

2
tvanfosson

Vous pouvez probablement le changer dans votre IDE (dans les préférences Eclipse -> Java -> Compilateur -> Erreurs/Avertissements)

1
Dutow

Il n'y a pas d'option pour ça. Dans Java (comme beaucoup d'autres lang.), Vous pouvez avoir accès à tous les membres statiques d'une classe via son nom de classe ou l'objet instance de cette classe. Cela dépend de vous et de votre cas et solution logicielle que vous devez utiliser qui vous donne plus de lisibilité.

0
Pooria