Les méthodes d'assistance privées doivent-elles être statiques si elles peuvent l'être

205

Disons que j'ai une classe conçue pour être instanciée. J'ai plusieurs méthodes "d'assistance" privées à l'intérieur de la classe qui ne nécessitent aucun accès à aucun des membres de la classe et fonctionnent uniquement sur leurs arguments, renvoyant un résultat.

public class Example {
   private Something member;

   public double compute() {
       double total = 0;
       total += computeOne(member);
       total += computeMore(member);
       return total;         
   }

   private double computeOne(Something arg) { ... }
   private double computeMore(Something arg) {... } 
} 

Y a-t-il une raison particulière de spécifier computeOneet en computeMoretant que méthodes statiques - ou une raison particulière de ne pas le faire?

Il est certainement plus facile de les laisser comme non statiques, même s'ils peuvent certainement être statiques sans causer de problèmes.

avalys
la source
3
Voir aussi quand ne pas utiliser le mot-clé statique en Java: stackoverflow.com/questions/1766715/…
Mark Butler

Réponses:

174

Je préfère que ces méthodes d'aide soient private static; ce qui indiquera clairement au lecteur qu'il ne modifiera pas l'état de l'objet. Mon IDE montrera également les appels aux méthodes statiques en italique, donc je saurai que la méthode est statique sans regarder la signature.

Esko Luontola
la source
2
Un de mes fils préférés: j'utilise NetBeans et il montre les appels de méthode statique avec des polices italiques.
skiabox
2
Je déclare normalement ces méthodes d'assistance comme protected staticce qui les rend testables très facilement dans une classe de test dans le même package.
James
2
@James ou simplement static(si nous voulons juste le tester).
mrod
3
"Ne modifiera pas l'état de l'objet" - explication parfaite de la façon de le différencier d'une méthode privée.
HopeKing
110

Cela pourrait entraîner un bytecode légèrement plus petit, car les méthodes statiques n'auront pas accès à this. Je ne pense pas que cela fasse une différence de vitesse (et si c'était le cas, ce serait probablement trop petit pour faire une différence dans l'ensemble).

Je les rendrais statiques, car je le fais généralement si possible. Mais c'est juste moi.


EDIT: Cette réponse ne cesse de faire l'objet d'un vote négatif, peut-être en raison de l'assertion non étayée de la taille du bytecode. Je vais donc réellement effectuer un test.

class TestBytecodeSize {
    private void doSomething(int arg) { }
    private static void doSomethingStatic(int arg) { }
    public static void main(String[] args) {
        // do it twice both ways
        doSomethingStatic(0);
        doSomethingStatic(0);
        TestBytecodeSize t = new TestBytecodeSize();
        t.doSomething(0);
        t.doSomething(0);
    }
}

Bytecode (récupéré avec javap -c -private TestBytecodeSize):

Compiled from "TestBytecodeSize.java"
class TestBytecodeSize extends java.lang.Object{
TestBytecodeSize();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   return

private void doSomething(int);
  Code:
   0:   return

private static void doSomethingStatic(int);
  Code:
   0:   return

public static void main(java.lang.String[]);
  Code:
   0:   iconst_0
   1:   invokestatic    #2; //Method doSomethingStatic:(I)V
   4:   iconst_0
   5:   invokestatic    #2; //Method doSomethingStatic:(I)V
   8:   new     #3; //class TestBytecodeSize
   11:  dup
   12:  invokespecial   #4; //Method "<init>":()V
   15:  astore_1
   16:  aload_1
   17:  iconst_0
   18:  invokespecial   #5; //Method doSomething:(I)V
   21:  aload_1
   22:  iconst_0
   23:  invokespecial   #5; //Method doSomething:(I)V
   26:  return

}

L'appel de la méthode statique prend deux octets (octets?): iconst_0(Pour l'argument) et invokestatic.
L'invocation de la méthode non statique en prend trois: aload_1(pour l' TestBytecodeSizeobjet, je suppose), iconst_0(pour l'argument), et invokespecial. (Notez que si ceux - ci n'avaient pas des méthodes privées, il aurait été au invokevirtuallieu de invokespecial, voir JLS Méthodes §7.7 Invocation .)

Maintenant, comme je l'ai dit, je ne m'attends pas à ce qu'il y ait une grande différence de performances entre ces deux, à part le fait que cela invokestaticnécessite un bytecode de moins. invokestaticet invokespecialdevraient tous deux être légèrement plus rapides que invokevirtual, car ils utilisent tous deux une liaison statique au lieu de dynamique, mais je n'ai aucune idée si l'un est plus rapide que l'autre. Je ne trouve pas non plus de bonnes références. Le plus proche que je peux trouver est cet article de JavaWorld de 1997 , qui reprend essentiellement ce que je viens de dire:

Les instructions les plus rapides seront probablement invokespecialet invokestatic, car les méthodes invoquées par ces instructions sont liées statiquement. Lorsque la JVM résout la référence symbolique de ces instructions et la remplace par une référence directe, cette référence directe inclura probablement un pointeur vers les bytecodes réels.

Mais beaucoup de choses ont changé depuis 1997.

Donc en conclusion ... Je suppose que je reste fidèle à ce que j'ai dit auparavant. La vitesse ne devrait pas être la raison de choisir l'un plutôt que l'autre, car ce serait au mieux une micro-optimisation.

Michael Myers
la source
tous ces votes négatifs pour ce qui semble être une réponse très simple. +1 dans l'intérêt de la vérité, de la justice, ....
Steve B.
Merci. (Bien que j'aurais pu le supprimer et obtenir un badge alors qu'il était à -3.: D)
Michael Myers
2
Le compilateur JIT les incorporera et les optimisera de toute façon, donc la quantité d'instructions de bytecode n'a pas grand-chose à voir avec la vitesse à laquelle elles seront.
Esko Luontola
3
C'est pourquoi j'ai dit "Je ne pense pas que cela fasse une différence de vitesse (et si c'était le cas, ce serait probablement trop petit pour faire une différence dans l'ensemble)."
Michael Myers
7
Avant de vous lancer dans des micro-optimisations de ce type, vous devez considérer l'effet du compilateur de points chauds. Conclusion: écrivez votre code pour la lisibilité par les humains et laissez l'optimisation au compilateur
Ichthyo
18

Ma préférence personnelle serait de les déclarer statiques, car c'est un indicateur clair qu'ils sont apatrides.

Steve B.
la source
18

La réponse est ... cela dépend.

Si le membre est une variable d'instance spécifique à l'objet que vous traitez, alors pourquoi la passer comme paramètre?

Par exemple:

public class Example {
   private Something member;

   public double compute() {
       double total = 0;
       total += computeOne();
       total += computeMore();
       return total;         
   }

   private double computeOne() { /* Process member here */ }
   private double computeMore() { /* Process member here */ } 
}
Powerlord
la source
5
+1: Même si l'exemple est mauvais, trop de méthodes qui POURRAIENT être statiques sont une mauvaise odeur de code. Soit dit en passant, les méthodes statiques sont en fait des fonctions, elles ne sont pas du tout OO. Ils peuvent appartenir en tant que méthode dans une autre classe, ou vous pouvez manquer une classe à laquelle cette fonction pourrait appartenir en tant que méthode.
Bill K
11

L'une des raisons pour lesquelles vous pouvez déclarer des méthodes d'assistance statiques est si vous devez les appeler dans le constructeur de classe "avant" thisou super. Par exemple:

public class MyClass extends SomeOtherClass { 
    public MyClass(String arg) {
       super(recoverInt(arg));
    }

    private static int recoverInt(String arg) {
       return Integer.parseInt(arg.substring(arg.length() - 1));
    }
}

Ceci est un peu un exemple artificiel mais ne recoverIntpeut clairement pas être une méthode d'instance dans ce cas.

oxbow_lakes
la source
vous pouvez appeler une méthode d'instance dans le constructeur, mais si vous déléguez à un autre constructeur avec super (...) ou ceci (...), vous ne pouvez appeler une méthode d'instance qu'après la délégation (mais les statiques sont ok comme vous le signalez)
Kip
10

Je ne peux pas vraiment penser à des avantages clairs pour la méthode statique privée. Cela étant dit, il n'y a pas non plus d'avantages spécifiques à les rendre non statiques. C'est principalement une question de présentation: vous voudrez peut-être les rendre statiques pour souligner clairement qu'ils ne modifient pas un objet.

Pour une méthode avec différents privilèges d'accès, je pense qu'il y a deux arguments principaux:

  • les méthodes statiques peuvent être appelées sans créer d'instance d'un objet, ce qui peut être utile
  • les méthodes statiques ne peuvent pas être héritées, ce qui peut être un problème si vous avez besoin de polymorphisme (mais n'est pas pertinent pour les méthodes privées).

En plus de cela, la différence est assez petite, et je doute fortement que le supplément de ce pointeur passé à la méthode d'instance fasse une différence significative.

Axelle Ziegler
la source
4
L'avantage de ne pas le rendre statique est que quelqu'un peut sous-classer et remplacer le comportement de la méthode.
Clint Miller
7
Clint est une méthode privée afin que vous ne puissiez pas remplacer la méthode.
Bhushan Bhangale
Exactement, pour la méthode privée, je ne pense pas que cela fasse une grande différence. Pour les méthodes publiques, cependant, je suis d'accord avec ce que vous dites
Axelle Ziegler
8

La bonne réponse est:

toute méthode qui ne prend aucune information d'un champ et ne met aucune information dans un champ ne doit pas être une méthode d'instance. Toute méthode qui n'utilise ni ne modifie aucun champ de sa classe ou de son objet peut également être une méthode statique.

Gyan Singh
la source
5

ou pour une raison particulière de ne pas [les déclarer statiques]?

Oui.

En les conservant comme méthodes d'instance, vous vous autorisez à fournir une implémentation différente ultérieurement.

Cela peut sembler idiot (et ce serait le cas si ces méthodes ne sont utilisées que par vous dans un programme de 50 lignes) mais dans des applications plus grandes ou dans des bibliothèques utilisées par quelqu'un d'autre, vous pouvez décider de choisir une meilleure implémentation, mais ne le faites pas veulent casser le code existant.

Vous créez donc une sous-classe et renvoyez cela dans les nouvelles versions, et puisque les méthodes ont été déclarées comme méthodes d'instance, vous laissez simplement le polymorphisme faire son travail.

En outre, vous pouvez bénéficier de la confidentialité du constructeur et fournir une méthode d'usine statique pour la même raison.

Donc, ma recommandation est de les conserver en tant que méthodes d'instance, et d'éviter le statique si possible.
Profitez du dynamisme du langage.

Voir ici pour une vidéo quelque peu connexe: Comment concevoir une bonne API et pourquoi elle est importante

Bien qu'il ne soit pas directement lié à la discussion sur la méthode "statique vs instance", il touche à certains points intéressants de la conception de l'API.

OscarRyz
la source
1
Entièrement d'accord. J'essaie généralement d'éviter la statique et les soldats pour cette raison. Je pense que la plupart des autres réponses ont manqué le point.
Clint Miller
22
Nous parlons d' aides privés , ce qui signifie qu'ils ne font pas partie de l'API publique de la classe. Cela signifie que rien ne vous empêche de fournir une implémentation différente plus tard, de les rendre non statiques ou d'apporter d'autres modifications.
Jonik
Aaaahmm ... c'est un bon point Jonik. Pourtant, créer une bonne habitude devrait être une raison suffisante (subjectivement parlant bien sûr)
OscarRyz
2
Complètement en désaccord. Il n'y a aucune bonne raison pour laquelle vous voudriez changer une méthode privée sans changer la classe dans laquelle elle est définie. La seule façon de faire ce que vous suggérez est la manipulation de code octet.
Peter Lawrey
2
Ce que vous proposez pourrait avoir un sens si la méthode n'était pas privée, mais même dans ce cas, je vous suggérerais de concevoir en fonction d'un besoin actuel plutôt que de concevoir en fonction d'un besoin imaginé.
Peter Lawrey
5

Un problème lié aux méthodes statiques est qu'elles peuvent rendre l'objet plus difficile à utiliser dans les tests unitaires . Mockito ne peut pas créer de maquette pour les méthodes statiques et vous ne pouvez pas créer une implémentation de sous-classe de la méthode.

Jon Onstott
la source
2
Vous ne devez pas écrire de tests pour des méthodes privées. Voyez ici . Il n'est pas nécessaire de tester une méthode privée, qu'elle soit statique ou non.
BW
@BW C'est une chose très subjective. Il existe de nombreuses raisons valables pour tester une méthode privée.
ruohola
4

Si la méthode est fondamentalement juste un sous-programme qui n'utilisera jamais de manière prévisible les informations d'état, déclarez-la statique.

Cela lui permet d'être utilisé dans d'autres méthodes statiques ou dans l'initialisation de classe, c'est-à-dire:

public class Example {
   //...

   //Only possible if computeOne is static
   public final static double COMPUTED_ONE = computeOne(new Something("1"));

   //...
}
Kip
la source
3

La question statique / non statique se résume à "aurai-je vraiment besoin d'utiliser un objet de cette classe"?

Alors, passez-vous l'objet entre différentes méthodes? L'objet contient-il des informations utiles en dehors du contexte des méthodes statiques? Y a-t-il une raison de ne pas définir les méthodes dans les deux sens si vous les utilisez dans les deux sens?

Si vous êtes dans ce dilemme, il me semble que vous disposez de toutes les données requises pour la méthode flottant dans votre code en dehors de l'objet. c'est ce que tu veux? Serait-il plus facile de simplement toujours collecter ces données dans un objet à chaque fois? Vous pourriez simplement être ambivalent quant à la validation d'un modèle unique. Si vous pouvez tout faire dans un sens, choisissez soit statique ou non statique et allez-y.

pas pas
la source
3

Ma préférence dans des cas comme ceux - ci est de faire computeOneet les computeMoreméthodes statiques. La raison: l'encapsulation. Moins il y a de code qui a accès à l'implémentation de votre classe, mieux c'est.

Dans l'exemple que vous donnez, vous déclarez cela computeOneet computeMorevous ne devriez pas avoir besoin d'accéder aux internes de la classe, alors pourquoi donner aux responsables de la classe la possibilité de se mêler des internes.

maxaposteriori
la source
3

Je voudrais clarifier peu de choses que d'autres affiches ont dites comme donnant de fausses informations.

Premièrement, puisque les méthodes sont privées même si vous les déclarez statiques, vous ne pourrez pas y accéder en dehors de cette classe. Deuxièmement, ils sont privés, vous ne pouvez donc même pas remplacer la sous-classe, donc statique ou non statique ne fait aucune différence. Troisièmement, une méthode privée non statique peut également être appelée à partir d'un constructeur de la classe, elle n'a pas besoin d'être statique.

Venons-en maintenant à votre question de savoir si une méthode d'assistance privée doit être définie comme statique ou non statique. Je vais aller avec la réponse de Steve car le marquage d'une méthode privée statique montre que cette méthode est sans état car je respecte également cette règle lorsque je code.

Bhushan Bhangale
la source
mais il y a des endroits où seule une méthode statique peut être appelée, comme dans mon exemple et dans l'exemple de Chris Marshall
Kip
Oui Kip your et les réponses de Chris sont correctes. Je n'ai pas commenté ces réponses car ce sont des réponses correctes. Je n'ai parlé que de mauvaises réponses.
Bhushan Bhangale
3

D'après l'expérience, je dirais que ces méthodes privées ont tendance à être assez universelles et réutilisables.

Je pense que la première chose à faire est de se demander si la méthode peut être utile en dehors du contexte de classe actuel. Si c'est le cas, je ferais exactement ce que tout le monde suggère et extrairais cette méthode comme statique pour certaines classes d'utilitaires où quelqu'un devrait, espérons-le, vérifier avant d'implémenter une nouvelle méthode faisant exactement la même chose.

De telles méthodes privées à usage général sont à l'origine d'une grande partie de la duplication de code dans le projet car chaque développeur les réinvente indépendamment juste à l'endroit où il doit l'utiliser. La centralisation de ces méthodes est donc une voie à suivre.

Piotr Sobczyk
la source
2

Plus précisément à l'exemple que vous avez donné, il semble que le but de la définition de ces méthodes soit plus pour la clarté du code lorsque vous le lisez que pour la fonctionnalité (elles sont définies comme privées). Dans ce cas, aller avec statique ne fait vraiment rien pour vous, car le but de statique est d'exposer les fonctionnalités de classe.

pas pas
la source
Cette petite réponse, enfouie sous une charge d'autres réponses, touche vraiment l'essence de la question: fonctionnalité au niveau de la classe vs fonctionnalité au niveau de l'objet.
eljenso
Merci, el, je l'apprécie vraiment. Cela ne me dérangerait pas un petit vote, non plus :)
pas le
Je lui ai donné un +1 au moment où j'ai écrit le commentaire.
eljenso
1
Je ne suis pas d'accord. La clarté du code est toujours importante pour les méthodes privées. C'est peut-être moins important, mais c'est toujours quelque chose à rechercher.
LegendLength
1

L'une des raisons est que, toutes choses étant égales par ailleurs, les appels de méthode statique devraient être plus rapides. Les méthodes statiques ne peuvent pas être virtuelles et ne prennent pas implicitement cette référence.

dsimcha
la source
Voir ma réponse ci-dessous (j'ai ajouté l'analyse après qu'elle était déjà à -3, donc ce n'est pas très visible).
Michael Myers
1

Comme beaucoup de gens l'ont dit, faites-le comme un statique ! Voici la règle générale que je suis: si vous pensez que la méthode est juste une fonction mathématique, c'est-à-dire qu'elle est sans état, n'implique aucune variable d'instance (=> pas de variable de couleur bleue [dans l'éclipse] dans la méthode), et le résultat de la méthode sera la même pour le nombre «n» d'appels (avec les mêmes paramètres, bien sûr), puis marquez cette méthode comme STATIQUE.

Et si vous pensez que cette méthode sera utile à une autre classe, déplacez-la dans une classe Util dans le cas contraire, mettez la méthode en privé dans la même classe. (minimiser l'accessibilité)

I have
la source
1

Hors sujet: je conserverais les méthodes d'assistance dans une classe utilitaire / assistant autonome avec uniquement des méthodes statiques.

Le problème avec les méthodes d'aide au point d'utilisation (lire «même classe») est qu'une personne en bas de la ligne peut simplement choisir de publier ses propres méthodes d'aide non liées au même endroit

Toutes les personnes
la source
-1: SO n'est pas un forum de messagerie. Vous ferez mieux en posant réellement une bonne question.
Stu Thompson
1
Je préférerais avoir les méthodes d'assistance avec la classe à laquelle elles sont liées / liées en tant que méthodes statiques. Plus encore, je peux en exposer certains en public s'ils doivent être utilisés en externe. De cette façon, il sera plus facile de maintenir l'API pour cette classe, si elle est censée être exposée publiquement et utilisée de manière intensive.
Ivaylo Slavov
1
class Whatever {

    public static varType myVar = initializeClassVariable();

    private static varType initializeClassVariable() {

        //initialization code goes here
    }
}

L'avantage des méthodes statiques privées est qu'elles peuvent être réutilisées ultérieurement si vous devez réinitialiser la variable de classe.

mug896
la source
1
  1. Sans le modificateur statique, vous ne pouvez pas comprendre que la méthode est sans état sans analyse supplémentaire qui peut être facilement effectuée lorsque vous (ré) écrivez la méthode.

  2. Ensuite, le modificateur "statique" peut vous donner des idées sur la refactorisation en plus d'autres choses que d'autres peuvent trouver inutiles. Par exemple, déplacer la méthode vers une classe Utility ou la convertir en méthode membre.

bodrine
la source
0

Je les déclarerais comme statiques pour les signaler comme apatrides.

Java n'a pas de meilleur mécanisme pour les opérations mineures qui ne sont pas exportées, donc je pense que la statique privée est acceptable.

Uri
la source