I metodi di supporto privato devono essere statici se possono essere statici


205

Diciamo che ho una classe progettata per essere istanziata. Ho diversi metodi "helper" privati ​​all'interno della classe che non richiedono l'accesso a nessuno dei membri della classe e opero esclusivamente sui loro argomenti, restituendo un risultato.

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) {... } 
} 

C'è qualche motivo particolare per specificare computeOnee computeMorecome metodi statici - o qualche motivo particolare per non farlo?

È certamente più facile lasciarli come non statici, anche se potrebbero sicuramente essere statici senza causare problemi.


3
Vedi anche quando non usare la parola chiave static in Java: stackoverflow.com/questions/1766715/…
Mark Butler,

Risposte:


174

Preferisco che tali metodi di supporto siano private static; che chiarirà al lettore che non modificheranno lo stato dell'oggetto. Il mio IDE mostrerà anche le chiamate ai metodi statici in corsivo, quindi saprò che il metodo è statico senza guardare la firma.


2
Uno dei miei thread preferiti. Sto usando NetBeans e mostra chiamate di metodo statiche con caratteri corsivi.
skiabox,

2
Normalmente dichiaro quei metodi di supporto protected staticche li rendono facilmente testabili in una classe di test nello stesso pacchetto.
James,

2
@James o semplicemente static(se lo vogliamo solo per i test).
mrod

3
"Non modificherà lo stato dell'oggetto" spiegazione perfetta di come differenziarlo da un metodo privato.
HopeKing,

110

Potrebbe comportare un bytecode leggermente più piccolo, poiché i metodi statici non avranno accesso a this. Non penso che faccia alcuna differenza di velocità (e se lo facesse, probabilmente sarebbe troppo piccolo per fare la differenza nel complesso).

Li renderei statici, dal momento che generalmente lo faccio se possibile. Ma sono solo io.


EDIT: questa risposta continua a essere sottovalutata, probabilmente a causa dell'asserzione non comprovata sulla dimensione del bytecode. Quindi eseguirò effettivamente 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 (recuperato con 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

}

Il richiamo del metodo statico richiede due bytecode (byteops?): iconst_0(Per l'argomento) e invokestatic.
Richiamare il metodo non statico richiede tre: aload_1(per l' TestBytecodeSizeoggetto, suppongo), iconst_0(per l'argomento) e invokespecial. (Nota che se questi non fossero stati metodi privati, sarebbe stato invokevirtualinvece di invokespecial; vedi JLS §7.7 Metodi di invocazione .)

Ora, come ho detto, non mi aspetto che ci sia una grande differenza nelle prestazioni tra questi due, oltre al fatto che invokestaticrichiede un bytecode in meno. invokestatice invokespecialdovrebbero essere entrambi leggermente più veloci di invokevirtual, poiché entrambi usano il binding statico anziché dinamico, ma non ho idea se uno dei due sia più veloce dell'altro. Non riesco a trovare buoni riferimenti neanche. Il più vicino che riesco a trovare è questo articolo JavaWorld del 1997 , che sostanzialmente ribadisce ciò che ho appena detto:

Molto probabilmente saranno le istruzioni più veloci invokespeciale invokestatic, poiché i metodi invocati da queste istruzioni sono associati staticamente. Quando la JVM risolve il riferimento simbolico per queste istruzioni e lo sostituisce con un riferimento diretto, tale riferimento diretto probabilmente includerà un puntatore ai bytecode effettivi.

Ma molte cose sono cambiate dal 1997.

Quindi, in conclusione ... Immagino di essere ancora fedele a quello che ho detto prima. La velocità non dovrebbe essere la ragione per scegliere l'una rispetto all'altra, poiché sarebbe al massimo una micro-ottimizzazione.


tutti questi voti negativi per quella che sembra una risposta molto semplice. +1 nell'interesse della verità, della giustizia, ....
Steve B.,

Grazie. (Anche se avrei potuto cancellarlo e ottenere un badge mentre era a -3.: D)
Michael Myers

2
Il compilatore JIT sarà comunque integrato e ottimizzato, quindi la quantità di istruzioni bytecode ha poco a che fare con la velocità con cui sarà.
Esko Luontola,

3
Ecco perché ho detto "Non penso che faccia alcuna differenza di velocità (e se lo facesse, probabilmente sarebbe troppo piccolo per fare la differenza nel complesso)."
Michael Myers

7
Prima di impegnarti in qualsiasi micro ottimizzazione di quel tipo, dovresti considerare l'effetto del compilatore di hot spot. In
conclusione

18

La mia preferenza personale sarebbe quella di dichiararli statici, in quanto è una chiara bandiera che sono apolidi.


18

La risposta è, dipende.

Se il membro è una variabile di istanza specifica dell'oggetto con cui hai a che fare, allora perché passarlo come parametro?

Per esempio:

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 */ } 
}

5
+1: anche se l'esempio è negativo, troppi metodi che POTREBBERO essere statici sono un cattivo odore di codice. A proposito, i metodi statici sono in realtà funzioni, non lo sono affatto OO. Possono appartenere a un metodo in un'altra classe oppure è possibile che manchi una classe a cui questa funzione potrebbe appartenere come metodo.
Bill K,

11

Uno dei motivi per cui potresti voler dichiarare metodi di supporto statici è se devi chiamarli nel costruttore della classe "prima" thiso super. Per esempio:

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));
    }
}

Questo è un esempio un po 'inventato, ma chiaramente recoverIntnon può essere un metodo di istanza in questo caso.


puoi chiamare un metodo di istanza nel costruttore, ma se stai delegando a un altro costruttore con super (...) o questo (...), non puoi chiamare un metodo di istanza fino a dopo la delega (ma le statistiche sono ok come fai notare)
Kip,

10

Non riesco davvero a pensare a chiari vantaggi per il metodo statico privato. Detto questo, non ci sono vantaggi specifici nel renderli neanche statici. È principalmente una questione di presentazione: potresti volerli rendere statici per sottolineare chiaramente il fatto che non stanno alterando un oggetto.

Per un metodo con privilegi di accesso diversi, penso che ci siano due argomenti principali:

  • i metodi statici possono essere chiamati senza creare un'istanza di un oggetto, il che può essere utile
  • i metodi statici non possono essere ereditati, il che può costituire un problema se è necessario il polimorfismo (ma è irrilevante per i metodi privati).

Oltre a ciò, la differenza è piuttosto piccola e dubito fortemente che l'aggiunta di questo puntatore al metodo di istanza faccia una differenza significativa.


4
Il vantaggio di non renderlo statico è che qualcuno può sottoclassare e sovrascrivere il comportamento del metodo.
Clint Miller,

7
Clint è un metodo privato, quindi non è possibile ignorare il metodo.
Bhushan Bhangale,

Esatto, per il metodo privato non penso che faccia molta differenza. Per i metodi pubblici, tuttavia, sono d'accordo con quello che stai dicendo
Axelle Ziegler,

8

La risposta corretta è:

qualsiasi metodo che non prende alcuna informazione da un campo e che non inserisce alcuna informazione in un campo non deve essere un metodo di istanza. Qualsiasi metodo che non utilizza o altera alcun campo nella sua classe o oggetto potrebbe anche essere un metodo statico.


5

o qualche motivo particolare per non [dichiararli come statici]?

Sì.

Conservandoli come metodi di istanza, ti consenti di fornire un'implementazione diversa in un secondo momento.

Può sembrare sciocco (e in realtà lo sarebbe se quei metodi fossero usati solo da te in un programma a 50 righe) ma in applicazioni più grandi, o in librerie usate da qualcun altro, potresti decidere di scegliere un'implementazione migliore, ma non farlo vuoi rompere il codice esistente.

Quindi crei una sottoclasse e la restituisci nelle nuove versioni, e poiché i metodi sono stati dichiarati come metodi di istanza, lasci che il polimorfismo faccia il suo lavoro.

Inoltre, potresti trarre vantaggio dal rendere privato il costruttore e fornire un metodo factory statico per lo stesso motivo.

Quindi, la mia raccomandazione è di mantenerli come metodi di istanza ed evitare statici se possibile.
Approfitta del dinamismo che la lingua offre.

Guarda qui per un video in qualche modo correlato: come progettare una buona API e perché è importante

Sebbene non sia direttamente correlato alla discussione sul metodo "statico vs istanza", tocca alcuni punti interessanti nella progettazione dell'API.


1
Sono completamente d'accordo. Di solito cerco di evitare la statica e i privati ​​del tutto per questo motivo. Penso che la maggior parte delle altre risposte abbia perso il punto.
Clint Miller,

22
Stiamo parlando di aiutanti privati , il che significa che non fanno parte dell'API pubblica della classe. Ciò significa che nulla ti impedisce di fornire un'implementazione diversa in un secondo momento, rendendole non statiche o apportando altre modifiche.
Jonik,

Aaaahmm ... è un buon punto Jonik. Tuttavia, creare una buona abitudine dovrebbe essere una ragione sufficiente (soggettivamente parlando ovviamente)
OscarRyz,

2
Completamente in disaccordo. Non ci sono buoni motivi per voler cambiare un metodo privato senza cambiare la classe in cui è definito. L'unico modo per fare ciò che suggerisci è la manipolazione del codice byte.
Peter Lawrey,

2
Ciò che suggerisci potrebbe avere senso se il metodo non fosse privato, ma anche allora ti suggerirei di progettare sulla base di una necessità attuale piuttosto che progettare sulla base di un'esigenza immaginata.
Peter Lawrey,

5

Un problema relativo all'avere metodi statici è che può rendere l'oggetto più difficile da usare nei test unitari . Mockito non può creare simulazioni per metodi statici e non è possibile creare un'implementazione di sottoclasse del metodo.


2
Non dovresti scrivere test per metodi privati. Vedi qui . Non è necessario testare un metodo privato, statico o meno.
BW,

@BW Questa è una cosa molto soggettiva. Esistono molti motivi validi per testare un metodo privato.
ruohola,

4

Se il metodo è fondamentalmente solo una subroutine che non utilizzerà mai prevedibilmente le informazioni sullo stato, dichiararlo statico.

Ciò consente di utilizzarlo in altri metodi statici o nell'inizializzazione della classe, ovvero:

public class Example {
   //...

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

   //...
}

3

La domanda statica / non statica si riduce a "dovrò davvero usare un oggetto di questa classe"?

Quindi, stai passando l'oggetto tra metodi diversi? L'oggetto contiene informazioni utili al di fuori del contesto dei metodi statici? C'è qualche motivo per non definire i metodi in entrambi i modi se li userai in entrambi i modi?

Se ti trovi in ​​questo dilemma, mi sembra che tu abbia tutti i dati richiesti per il metodo fluttuante nel tuo codice al di fuori dell'oggetto. È questo che vuoi? Sarebbe più semplice raccogliere sempre quei dati in un oggetto ogni volta? Potresti essere semplicemente ambivalente nel impegnarti in un singolo modello. Se riesci a fare tutto usando un solo modo, scegli statico o non statico e seguilo.


3

La mia preferenza in casi come questi è quello di rendere computeOnee computeMoremetodi statici. Il motivo: incapsulamento. Meno codice ha accesso all'implementazione della tua classe, meglio è.

Nell'esempio si dà, affermate che computeOnee computeMorenon dovrebbe essere necessario per accedere alle parti interne della classe, quindi perché dare la possibilità a manutentori della classe di immischiarsi con le parti interne.


3

Vorrei chiarire alcune cose che altri manifesti hanno detto come fornire informazioni sbagliate.

Innanzitutto poiché i metodi sono privati ​​anche se li dichiari statici, non potrai accedervi al di fuori di questa classe. In secondo luogo sono privati, quindi non è nemmeno possibile eseguire l'override in una sottoclasse in modo che statico o non statico non faccia alcuna differenza. In terzo luogo un metodo privato non statico può essere chiamato anche da un costruttore della classe, non è necessario che sia statico.

Ora stai arrivando alla tua domanda se un metodo di supporto privato dovrebbe essere definito come statico o non statico. Andrò con la risposta di Steve poiché contrassegnare un metodo privato statico mostra che questo metodo è apolide poiché seguo anche questa regola quando scrivo.


ma ci sono luoghi in cui è possibile chiamare solo un metodo statico, come nel mio esempio e nell'esempio di Chris Marshall
Kip,

Sì, salta la tua e la risposta di Chris è corretta. Non ho commentato quelli in quanto sono risposte corrette. Ho parlato solo di risposte sbagliate.
Bhushan Bhangale,

3

Dall'esperienza vorrei dire che tali metodi privati ​​tendono ad essere abbastanza universali e riutilizzabili.

Penso che la prima cosa da fare sia porre la domanda se il metodo può essere utile al di fuori del contesto di classe corrente. In tal caso, farei esattamente ciò che tutti suggeriscono ed estrarre questo metodo come statico in alcune classi utils in cui qualcuno si spera che controlli prima di implementare un nuovo metodo facendo esattamente la stessa cosa.

Tali metodi privati ​​di uso generale sono fonte di gran parte della duplicazione del codice nel progetto perché ogni sviluppatore li reinventa in modo indipendente proprio nel luogo in cui deve usarlo. Quindi la centralizzazione di tali metodi è una strada da percorrere.


2

Più specificamente all'esempio che hai dato, sembra che lo scopo di definire questi metodi sia più per la chiarezza del codice durante la lettura che per la funzionalità ( sono definiti come privati). In tal caso, l'utilizzo di static non fa davvero nulla per te, poiché lo scopo di static è quello di esporre la funzionalità di classe.


Questa piccola risposta, sepolta sotto un carico di altre risposte, tocca davvero l'essenza della questione: funzionalità a livello di classe vs. funzionalità a livello di oggetto.
eljenso,

Grazie, lo apprezzo molto. Non mi dispiacerebbe nemmeno un po 'di voto :)
non il

Gli ho dato un +1 nel momento in cui ho scritto il commento.
eljenso,

1
Non sono d'accordo. La chiarezza del codice è ancora importante per i metodi privati. Potrebbe essere meno importante, ma è ancora qualcosa per cui lottare.
LegendLength

1

Uno dei motivi è che, a parità di condizioni, le chiamate ai metodi statici dovrebbero essere più veloci. I metodi statici non possono essere virtuali e non implicano questo riferimento.


Vedi la mia risposta di seguito (ho aggiunto l'analisi dopo che era già a -3, quindi non è molto visibile).
Michael Myers

1

Come molte persone hanno detto, renderlo statico ! Ecco la regola del pollice che seguo: se pensi che il metodo sia solo una funzione matematica, cioè è apolide, non coinvolge alcuna variabile di istanza (=> nessuna variazione di colore blu [in eclissi] nel metodo) e il risultato di il metodo sarà lo stesso per 'n' numero di chiamate (con gli stessi parametri, ovviamente), quindi contrassegnare quel metodo come STATICO.

E se ritieni che questo metodo sia utile ad altre classi, spostalo in una classe Util altrimenti, metti il ​​metodo come privato nella sameclass. (minimizzare l'accessibilità)


1

Off-Topic: terrei i metodi helper in una classe utility / helper autonoma con solo metodi statici.

Il problema di avere metodi di supporto nel punto di utilizzo (leggi 'stessa classe') è che qualcuno in fondo alla linea può semplicemente scegliere di pubblicare i propri metodi di supporto non correlati nello stesso posto


-1: SO non è un forum di messaggistica. Farai meglio facendo effettivamente una domanda corretta.
Stu Thompson,

1
Preferirei avere i metodi helper insieme alla classe a cui sono legati / correlati come statici. Ancora di più, posso esporne alcune come pubbliche se dovessero essere utilizzate esternamente. In questo modo sarà più semplice mantenere l'API per quella classe, se si suppone che sia esposto pubblicamente e utilizzato intensivamente.
Ivaylo Slavov

1
class Whatever {

    public static varType myVar = initializeClassVariable();

    private static varType initializeClassVariable() {

        //initialization code goes here
    }
}

Il vantaggio dei metodi statici privati ​​è che possono essere riutilizzati in un secondo momento se è necessario reinizializzare la variabile di classe.


1
  1. Senza il modificatore statico non è possibile capire che il metodo è apolide senza analisi aggiuntiva che può essere facilmente eseguita quando si (ri) scrive il metodo.

  2. Quindi il modificatore "statico" può darti idee sul refactoring oltre ad altre cose che altri potrebbero trovare inutili. Ad esempio, spostando il metodo in una classe Utility o convertendolo in un metodo membro.


0

Li dichiarerei statici per segnalarli come apolidi.

Java non ha un meccanismo migliore per le operazioni minori che non vengono esportate, quindi penso che la statica privata sia accettabile.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.