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 requièrent 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) {... }
}
Existe-t-il une raison particulière de spécifier computeOne
et computeMore
comme méthodes statiques - ou une raison particulière de ne pas le faire?
Il est certainement plus facile de les laisser non statiques, même s’ils pourraient rester statiques sans causer de problèmes.
Je préfère que ces méthodes d'assistance soient private static
; ce qui indiquera clairement au lecteur qu’ils ne modifieront pas l’état de l’objet. Mon IDE affichera également les appels aux méthodes statiques en italique, je saurai donc que la méthode est statique sans regarder la signature.
Cela pourrait donner 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 globale).
Je les rendrais statiques, puisque 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, probablement en raison de l'affirmation non corroborée de la taille du bytecode. Je vais donc faire 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 bytecodes (byteops?): iconst_0
(pour l'argument) et invokestatic
.
L'appel de la méthode non statique prend trois étapes: aload_1
(pour l’objet TestBytecodeSize
, je suppose), iconst_0
(pour l'argument), et invokespecial
. (Notez que s'il ne s'agissait pas de méthodes privées, il s'agirait de invokevirtual
au lieu de invokespecial
; voir JLS §7.7 méthodes d'appel .)
Maintenant, comme je l'ai dit, je ne m'attends pas à une grande différence de performances entre les deux, à part le fait que invokestatic
nécessite un bytecode de moins. invokestatic
et invokespecial
devraient tous deux être légèrement plus rapides que invokevirtual
, car ils utilisent tous les deux une liaison statique au lieu de dynamique, mais je ne sais pas si l'un est plus rapide que l'autre. Je ne trouve pas de bonnes références non plus. Le plus proche que je puisse trouver est cet article de JavaWorld de 1997 , ce qui résume ce que je viens de dire:
Les instructions les plus rapides seront probablement
invokespecial
etinvokestatic
, car les méthodes invoquées par ces instructions sont liées de manière statique. Lorsque la machine virtuelle Java 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 sur les codes byt réels.
Mais beaucoup de choses ont changé depuis 1997.
Donc, en conclusion ... Je suppose que je reste fidèle à ce que j'ai déjà dit. La rapidité ne devrait pas être la raison pour choisir l’une sur l’autre, ce serait au mieux une micro-optimisation.
Ma préférence personnelle serait de les déclarer statiques, car il est clair qu'ils sont apatrides.
La réponse est ... ça dépend.
Si member est une variable d'instance spécifique à l'objet avec lequel vous traitez, pourquoi la passer sous forme de 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 */ }
}
Une des raisons pour lesquelles vous voudrez peut-être déclarer des méthodes d'assistance statiques est si vous devez les appeler dans le constructeur de classe "avant" this
ou 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));
}
}
C'est un peu un exemple artificiel mais recoverInt
ne peut clairement pas être une méthode d'instance dans ce cas.
Je ne peux pas vraiment penser à des avantages clairs pour la méthode statique privée. Cela dit, il n’ya pas d’avantages particuliers à les rendre non statiques non plus. C’est principalement une question de présentation: vous pouvez les rendre statiques pour souligner clairement le fait qu’ils ne modifient pas un objet.
Pour la méthode avec des privilèges d'accès différents, je pense qu'il y a deux arguments principaux:
En plus de cela, la différence est assez petite et je doute fortement que le supplément que ce pointeur a passé à la méthode d'instance fasse une différence significative.
La bonne réponse est:
toute méthode qui ne prend aucune information dans un champ et ne met aucune information dans un champ ne doit pas nécessairement ê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 aussi bien être une méthode statique.
ou une raison particulière de ne pas [les déclarer comme statiques]?
Oui.
En les conservant comme méthodes d'instance, vous vous autorisez à fournir ultérieurement une implémentation différente.
Cela peut sembler idiot (et ce serait en fait le cas si ces méthodes ne sont utilisées que par vous dans un programme de 50 lignes), mais dans les applications plus importantes, ou dans les bibliothèques utilisées par quelqu'un d'autre, vous pouvez choisir une meilleure implémentation, mais ne le faites pas. vouloir casser le code existant.
Donc, vous créez une sous-classe et vous la renvoyez dans les nouvelles versions, et puisque les méthodes ont été déclarées en tant que méthodes d'instance, vous laissez simplement le polymorphisme faire son travail.
De plus, vous pouvez tirer parti du fait que le constructeur est privé et fournir une méthode de fabrique statique pour la même raison.
Donc, ma recommandation est de les conserver en tant que méthodes d'instance et d'éviter si possible les méthodes statiques.
Profitez du dynamisme offert par la langue.
Voir ici pour une vidéo quelque peu connexe: Comment concevoir une bonne API et pourquoi elle compte
Bien qu'elle ne soit pas directement liée à la discussion sur la méthode "statique vs instance", elle aborde certains points intéressants de la conception d'API.
Un problème avec les méthodes statiques est que cela peut rendre l’objet plus difficile à utiliser dans 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.
Si la méthode est fondamentalement juste un sous-programme qui n'utilisera jamais de manière prévisible des informations d'état, déclarez-les statiques.
Cela lui permet d’être utilisé dans d’autres méthodes statiques ou dans l’initialisation de classe, à savoir:
public class Example {
//...
//Only possible if computeOne is static
public final static double COMPUTED_ONE = computeOne(new Something("1"));
//...
}
Ma préférence dans de tels cas est de faire des méthodes computeOne
et computeMore
statiques. La raison: encapsulation. Le moins de code qui a accès à la mise en œuvre de votre classe, mieux ce sera.
Dans l'exemple que vous donnez, vous déclarez que computeOne
et computeMore
ne devraient pas avoir besoin d'accéder aux éléments internes de la classe, alors pourquoi donner aux responsables de la classe la possibilité de se mêler aux éléments internes.
J'aimerais clarifier quelques points que d'autres afficheurs ont dit comme donnant des informations erronées.
Premièrement, étant donné que 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, de sorte que vous ne pouvez même pas remplacer dans 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.
J'en viens maintenant à votre question 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 marquer une méthode privée montre que cette méthode est sans état, car je suis aussi cette règle quand je code.
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 tel est le cas, je ferais exactement ce que tout le monde suggère et extrairais cette méthode de manière statique pour une classe d'utils où quelqu'un vérifie, espérons-le, avant d'implémenter une nouvelle méthode, en faisant exactement la même chose.
Ces méthodes privées à usage général sont à l'origine d'une grande partie de la duplication de code dans les projets, car chaque développeur les réinvente de manière indépendante, juste à la place dont elle a besoin pour l'utiliser. La centralisation de telles méthodes est donc une voie à suivre.
La question statique/non statique se résume à "Dois-je vraiment 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 avez toutes les données nécessaires à 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 êtes peut-être simplement ambivalent de vous engager dans un seul modèle. Si vous pouvez tout faire d'une seule manière, choisissez alors statique ou non statique et allez-y.
Plus précisément, à l'exemple que vous avez donné, il semble que le but de la définition de ces méthodes soit davantage pour la clarté du code lorsque vous le lisez que pour la fonctionnalité (elles sont défini comme privé). Dans ce cas, l'utilisation de static ne fait vraiment rien pour vous, car le but de static est d'exposer les fonctionnalités de classe.
class Whatever {
public static varType myVar = initializeClassVariable();
private static varType initializeClassVariable() {
//initialization code goes here
}
}
L'avantage des méthodes statiques privées réside dans le fait qu'elles peuvent être réutilisées ultérieurement si vous devez réinitialiser la variable de classe.
Une des raisons est que, toutes choses étant égales par ailleurs, les appels aux méthodes statiques devraient être plus rapides. Les méthodes statiques ne peuvent pas être virtuelles et ne prennent pas implicitement cette référence.
Sans le modificateur statique, vous ne pouvez pas comprendre que la méthode est sans état sans analyse supplémentaire, ce qui peut être facilement fait lorsque vous écrivez (ré )z la méthode.
Ensuite, le modificateur "statique" peut vous donner des idées sur la refactorisation en plus d'autres choses que d'autres pourraient trouver inutiles. Par exemple. déplacer la méthode vers une classe d'utilitaires ou la convertir en méthode membre.
Hors sujet: je conserverais les méthodes d'assistance dans une classe utilitaire/d'assistance autonome avec uniquement des méthodes statiques.
Le problème avec l'utilisation de méthodes d'assistance au moment de l'utilisation (lire 'même classe') est qu'une personne en bout de ligne peut simplement choisir de publier ses propres méthodes d'assistance non liées au même endroit.
Je les déclarerais comme statiques pour les signaler comme apatrides.
Java ne dispose pas d'un meilleur mécanisme pour les opérations mineures qui ne sont pas exportées. Je pense donc que les transactions statiques privées sont acceptables.
Comme beaucoup de gens l'ont dit, faites comme statique ! Voici la règle du pouce que je suis: Si vous pensez que la méthode est juste une fonction mathématique , c’est-à-dire qu’elle est sans état, ne comporte aucune variable d’instance (= > pas de couleur bleue vars [dans Eclipse] dans la méthode), et le résultat de la méthode sera le même pour 'n' nombre d'appels (avec les mêmes paramètres, bien sûr), puis marquez cette méthode comme STATIC.
Et si vous pensez que cette méthode sera utile à une autre classe, déplacez-la dans une classe Util sinon, mettez la méthode comme privée dans la même classe. (minimisant l'accessibilité)