Pourquoi ne pas appeler une méthode statique par exemple une erreur pour le compilateur de Java?


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-il pas une erreur?

MODIFIER:

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 lorsque l'on discute de cela en raison du potentiel de vraiment gâcher les choses avec eux. Mais vraiment le problème est qu'une telle utilisation est toujours non-sens et vous ne pouvez pas (avec compétence) écrire un tel appel et dire qu'il. Tout exemple de ce type d'appel de méthode serait barmy. En voici un autre:

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

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

Author: tmtest, 2009-03-04

12 answers

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

Juste pour répondre au point de DJClayworth, voici ce qui est autorisé 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() j'attends qu'il utiliser la valeur de someVariable. Si SomeMethod() est une méthode statique, cette attente est invalide; le code me trompe. Comment peut éventuellement être un bon chose?

Assez 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 qu'il va utiliser est le type déclaré de la variable. C'est un désordre incohérent et inutile. Pourquoi permet-il?

EDIT: Cette modification est une réponse à la réponse de Clayton, qui prétend qu'elle autorise l'héritage pour les méthodes statiques. 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 de temps d'exécution de b est complètement ignorée.

 79
Author: Jon Skeet, 2009-03-04 21:23:47

Pourquoi devrait-il s'agir d'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éthode statiques. Il semble que vous obteniez le activeCount() pour le thread mentionné par thread, mais vous obtenez vraiment le nombre pour le thread appelant. C'est une erreur logique que vous en tant que programmeur sont de décisions. Émettre un avertissement est la chose appropriée pour le compilateur à 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 permet à 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 douteux, mais il vous donne l'avertissement pour vous assurer que vous êtes conscient qu'il pourrait causer des problèmes.

 15
Author: Bill the Lizard, 2009-03-04 14:14:56

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

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

Update: 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 l'ont rendudisponible uniquement si vous définissez explicitement le mode source sur "1.4". Je suppose que l'une pourrait faire il une erreur dans un nouveau mode source "java 7". Mais je doute qu'ils le fassent, étant donné que tous les tracas que cela causerait. Comme d'autres l'ont souligné, il n'est pas strictement nécessaire de vous empêcher d'écrire du code confus. Et les changements de langue à Java devraient être limités au strict nécessaire à ce stade.

 9
Author: Thilo, 2009-03-05 23:17:29

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

 6
Author: PaulJWilliams, 2009-03-04 13:08:09

Probablement pour la même logique qui ne fait pas une erreur:

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

    public void bar()
    {
        foo(); // no need to do X.foo();
    }
}
 2
Author: TofuBeer, 2009-03-04 13:13:02

La chose vraiment importante, du point de vue du compilateur, est qu'il soit capable de résoudre des symboles. Dans le cas d'une méthode statique, il doit savoir dans quelle classe la rechercher-car elle n'est associée à aucun objet 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 n'importe quelle méthode statique pour cet objet à partir de n'importe quelle instance de l'objet. Ils choisissent de permettre cela sw influencé, peut-être, par L'observation de @ TofuBeer to 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 que @TofuBeer mentionne, mais l'avoir autorisé ma position sur le fait de ne pas autoriser l'accès à partir d'une variable d'instance est moins tenable.

 2
Author: tvanfosson, 2009-03-04 13:23:08

Ce n'est pas une erreur car 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'invoquer une méthode statique dans la même classe sans les tracas. Puisque l'appel de x () est légal (même sans le nom de la classe self), appeler cela.x () devrait également être légal, et donc appeler via n'importe quel objet a également été rendu légal.

Cela aide également à encourager les utilisateurs à transformez les fonctions privées en fonctions statiques si elles ne changent pas d'état.

En outre, 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. Étant donné qu'une méthode statique ne change pas l'état ou ne se soucie pas de l'objet invoquant, elle ne provoque pas d'erreur réelle (juste de la confusion) pour permettre cela. Un avertissement suffit.

 2
Author: Uri, 2009-03-04 18:47:10

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

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

 2
Author: mP., 2009-03-04 20:02:39

Vous pouvez probablement le changer dans votreE (dans Eclipse Preferences -> Java -> Compiler -> Errors/Warnings)

 1
Author: Dutow, 2009-03-04 13:12:10

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. Ce serait à vous et à votre cas et à la solution logicielle que vous devriez utiliser pour vous donner plus de lisibilité.

 0
Author: Pooria, 2009-03-04 13:12:51

C'est un sujet assez ancien mais toujours à jour et apportant étonnamment un impact plus élevé de nos jours. Comme Jon l'a mentionné, ce n'est peut-être qu'une erreur des concepteurs de Java au tout début. Mais je n'imaginerais pas avant que cela puisse avoir un impact sur la sécurité.

De nombreux codeurs connaissent Apache Velocity, moteur de template flexible et puissant. Il est si puissant qu'il permet d'alimenter le modèle avec un ensemble d'objets nommés - strictement considérés comme des objets du langage de programmation (Java à l'origine). Ceux les objets sont accessibles à partir du modèle comme dans le langage de programmation, par exemple l'instance de chaîne de Java peut être utilisée avec tous ses champs publics, propriétés et méthodes

$input.isEmpty()

inputest une chaîne , s'exécute directement via la JVM et renvoie trueou false à la sortie de l'analyseur de vitesse). So far So good.

Mais en Java, tous les objets héritent de Object donc nos utilisateurs finaux peuvent également mettre cela dans le modèle

$input.getClass()

À obtenir une instance de String Classe.

Et avec cette référence, ils peuvent aussi appeler un static méthode forName(String) cette

$input.getClass().forName("java.io.FileDescriptor")

Utilisez n'importe quel nom de classe et utilisez-le pour tout ce que le compte du serveur Web peut faire (défacer, voler le contenu de la base de données, inspecter les fichiers de configuration, ...)

Cet exploit est en quelque sorte (dans un contexte spécifique) décrit ici: https://github.com/veracode-research/solr-injection#7-cve-2019-17558-rce-via-velocity-template-by-_s00py

Il ne serait pas possible si l'appel de méthodes statiques à partir d'une référence à l'instance de la classe était interdit.

Je ne dis pas qu'un cadre de programmation particulier est meilleur que l'autre, mais je veux juste mettre une comparaison. Il y a un port d'Apache Velocity pour. NET. En C # il n'est pas possible d'appeler des méthodes statiques juste à partir de la référence de l'instance quoi rend l'exploit comme ça inutile:

$input.GetType().GetType("System.IO.FileStream, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")
 0
Author: Tomasz Polachowski, 2020-02-28 08:50:54

Je considère juste ceci:

instanceVar.staticMethod();

Pour être raccourci pour cela:

instanceVar.getClass().staticMethod();

Si vous avez toujours dû faire ceci:

SomeClass.staticMethod();

Alors vous ne seriez pas en mesure 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 savoir quelle classe concrète l'instance est au moment de la compilation, seulement qu'elle implémente staticMethod() quelque part le long de la chaîne d'héritage.

EDIT: Cette réponse est fausse. Voir les commentaires pour détail.

 -9
Author: Clayton, 2009-03-04 21:56:19