Qual è il vantaggio nel dichiarare un metodo come statico


94

Recentemente ho esaminato i miei avvisi in Eclipse e ho trovato questo:

avviso statico

Darà un avviso al compilatore se il metodo può essere dichiarato statico.

[modifica] Citazione esatta all'interno della guida di Eclipse, con stress sul privato e finale:

Se abilitato, il compilatore emetterà un errore o un avviso per i metodi privati o finali e che fanno riferimento solo ai membri statici.

Sì, lo so che posso spegnerlo, ma voglio sapere il motivo per accenderlo?

Perché sarebbe una buona cosa dichiarare statico ogni metodo possibile?

Questo darà qualche vantaggio in termini di prestazioni? (in un dominio mobile)

Indicare un metodo come statico, suppongo stia dimostrando che non usi alcuna variabile di istanza, quindi potresti essere spostato in una classe di stile utils?

Alla fine della giornata dovrei semplicemente disattivare l'opzione "ignora" o dovrei correggere gli oltre 100 avvisi che mi ha fornito?

Pensi che queste siano solo parole chiave extra che sporcano il codice, dato che il compilatore inserirà comunque questi metodi inline? (un po 'come se non dichiarassi tutte le variabili che puoi finalizzare ma potresti ).


Non sono sicuro, ma questo potrebbe essere visto semplicemente come un aiuto alla programmazione. Gli avvertimenti sono semplicemente indicazioni di cose da guardare.
James P.

1
Sono curioso di sapere il tipo di funzionalità che questi metodi eseguono. Forse qualcosa non è stato fatto molto bene se ce ne sono così tanti.
ArjunShankar

Risposte:


132

Ogni volta che scrivi un metodo, esegui un contratto in un determinato ambito. Più ristretto è l'ambito, minori sono le possibilità che tu scriva un bug.

Quando un metodo è statico, non è possibile accedere a membri non statici; quindi, il tuo ambito è più ristretto. Quindi, se non hai bisogno e non avrai mai bisogno (anche nelle sottoclassi) di membri non statici per adempiere al tuo contratto, perché dare accesso a questi campi al tuo metodo? La dichiarazione del metodo staticin questo caso consentirà al compilatore di verificare che non si utilizzano membri che non si intende utilizzare.

Inoltre, aiuterà le persone che leggono il tuo codice a comprendere la natura del contratto.

Ecco perché è considerato opportuno dichiarare un metodo staticquando sta effettivamente implementando un contratto statico.

In alcuni casi, il tuo metodo significa solo qualcosa relativo a un'istanza della tua classe e accade che la sua implementazione non utilizzi effettivamente alcun campo o istanza non statica. In questi casi, non contrassegneresti il ​​metodo static.

Esempi di dove non useresti la staticparola chiave:

  • Un hook di estensione che non fa nulla (ma potrebbe fare qualcosa con i dati dell'istanza in una sottoclasse)
  • Un comportamento predefinito molto semplice pensato per essere personalizzabile in una sottoclasse.
  • Implementazione del gestore di eventi: l'implementazione varierà con la classe del gestore di eventi ma non utilizzerà alcuna proprietà dell'istanza del gestore di eventi.

1
+1 Si tratta di ridurre al minimo le opportunità di spararsi ai piedi e di ridurre quanto è necessario sapere per comprendere un metodo.
Peter Lawrey

7
Inoltre, non è necessario giocherellare con l'acquisizione di un'istanza solo per chiamare una funzione indipendente e autonoma.
Marko Topolnik

1
Quindi in altri mondi, qualsiasi metodo che non utilizza variabili di istanza dovrebbe essere dichiarato statico? Ho sempre pensato che i metodi dovrebbero essere statici solo se desiderabili (come i metodi di utilità).
Petr Mensik,

18
@PetrMensik Se un privatemetodo può essere dichiarato statico, quasi invariabilmente dovrebbe esserlo. Per qualsiasi altro livello di accesso, ci sono altri fattori da considerare, come l'invio dinamico.
Marko Topolnik

1
@ JamesPoulson Intendo l'invio del metodo dinamico, la cosa che accade durante l'esecuzione object.method()per selezionare il metodo da chiamare.
Marko Topolnik

15

Non esiste un concetto con l'ottimizzazione qui.

Un staticmetodo è staticperché dichiari esplicitamente che il metodo non si basa su alcuna istanza della classe che lo racchiude solo perché non è necessario. Quindi l'avviso di Eclipse, come indicato nella documentazione:

Se abilitato, il compilatore emetterà un errore o un avviso per i metodi privati ​​o finali e che fanno riferimento solo ai membri statici.

Se non hai bisogno di alcuna variabile di istanza e il tuo metodo è privato (non può essere chiamato dall'esterno) o finale (non può essere sovrascritto), non c'è motivo di lasciare che sia un metodo normale invece che statico. Un metodo statico è intrinsecamente più sicuro anche solo perché ti è permesso fare meno cose con esso (non ha bisogno di alcuna istanza, non hai alcun thisoggetto implicito ).


7

Non ho informazioni sulle prestazioni, suppongo sia leggermente migliore al massimo, dal momento che il codice non ha bisogno di fare un invio dinamico in base al tipo.

Tuttavia, un argomento molto più forte contro il refactoring in metodi statici è che attualmente l'utilizzo di static è considerato una cattiva pratica. Metodi / variabili statici non si integrano bene in un linguaggio orientato agli oggetti e inoltre, è difficile testarli adeguatamente. Questo è il motivo per cui alcuni linguaggi più recenti rinunciano del tutto al concetto di metodi / variabili statici, o cercano di interiorizzarli nel linguaggio in un modo che funzioni meglio con OO (ad esempio Oggetti in Scala).

La maggior parte delle volte, sono necessari metodi statici per implementare funzioni che utilizzano solo parametri come input e producono un output utilizzando quello (ad esempio funzioni di utilità / aiuto) Nei linguaggi moderni, esiste un concetto di funzione di prima classe che lo consente, quindi statico non è necessario. Java 8 avrà le espressioni lambda integrate, quindi ci stiamo già muovendo in questa direzione.


7
I metodi statici sono banali da testare (a patto che siano autonomi --- specialmente le funzioni pure, che sono l'obiettivo principale di un metodo statico). Il concetto OO non ha nulla da offrire in questo caso. Inoltre, il concetto di una funzione di prima classe ha poco o niente a che fare con il concetto di metodo statico. Se hai bisogno di una funzione di prima classe che faccia il lavoro di un metodo statico esistente, è questione di una manciata di caratteri per implementarla.
Marko Topolnik

5
L'osservazione della testabilità probabilmente non era orientata direttamente al metodo statico, ma i metodi statici sono molto più difficili da deridere, quindi complicano il test dei metodi che chiamano metodi statici.
Buhb

2
I metodi statici sono facili da deridere se si utilizza un potente framework di derisione come JMockit , PowerMock o Groovy .
Jeff Olson

Questi sono private staticmetodi quindi non avrai bisogno di prenderli in giro
Blundell

3

1. Metodo di dichiarazionestaticoffre un leggero vantaggio in termini di prestazioni, ma ciò che è più utile, consente di usarlo senza avere un'istanza di oggetto a portata di mano (si pensi ad esempio al metodo factory o all'ottenimento di un singleton). Serve anche allo scopo documentativo di raccontare la natura del metodo. Questo scopo documentativo non dovrebbe essere ignorato, poiché fornisce un suggerimento immediato sulla natura del metodo ai lettori del codice e agli utenti dell'API e serve anche come strumento di pensiero per il programmatore originale - essere espliciti sul significato previsto aiuta pensi anche in modo chiaro e produci un codice di qualità migliore (penso in base alla mia esperienza personale, ma le persone sono diverse). Ad esempio, è logico e quindi desiderabile distinguere tra metodi che operano su un tipo e metodi che agiscono su un'istanza del tipo (come sottolineato daJon Skeet nel suo commento a una domanda C # ).

Un altro caso d'uso per i staticmetodi è imitare l'interfaccia di programmazione procedurale. Pensa alla java.lang.System.println()classe, ai metodi e agli attributi in essa contenuti. La classe java.lang.Systemviene utilizzata come uno spazio dei nomi di raggruppamento piuttosto che un oggetto istanziabile.

2. Come può Eclipse (o qualsiasi altro tipo di entità programmata o di altro tipo - biocomposibile o non biocompostabile) sapere con certezza quale metodo potrebbe essere dichiarato statico? Anche se una classe base non accede a variabili di istanza o chiama metodi non statici, dal meccanismo dell'ereditarietà le cose possono cambiare. Solo se il metodo non può essere sovrascritto ereditando una sottoclasse, possiamo affermare con il 100% di certezza che il metodo può davvero essere dichiarato static. L'override di un metodo è impossibile esattamente nei due casi dell'essere

  1. private (nessuna sottoclasse può usarlo direttamente e nemmeno in linea di principio lo sa), o
  2. final (anche se accessibile dalla sottoclasse, non c'è modo di cambiare il metodo per fare riferimento a dati o funzioni dell'istanza).

Da qui la logica dell'opzione Eclipse.

3. Il poster originale chiede anche: " Indicare un metodo come statico, suppongo che mostri che non usi alcuna variabile di istanza, quindi potresti essere spostato in una classe di stile utils? " Questo è un ottimo punto. A volte questo tipo di modifica del design è indicato dall'avvertenza.

È un'opzione molto utile, che mi assicurerei di abilitare personalmente, se utilizzassi Eclipse e programmassi in Java.


1

Vedi la risposta di Samuel su come cambia l'ambito del metodo. Immagino che questo sia l'aspetto principale per rendere statico un metodo.

Hai anche chiesto informazioni sulle prestazioni:

Potrebbe esserci un piccolo miglioramento delle prestazioni, perché una chiamata a un metodo statico non necessita del riferimento implicito "this" come parametro.

Tuttavia, questo impatto sulle prestazioni è davvero minimo. Pertanto, è tutta una questione di portata.


1

Dalle linee guida sulle prestazioni di Android:

Preferisci statico al virtuale Se non hai bisogno di accedere ai campi di un oggetto, rendi il tuo metodo statico. Le invocazioni saranno circa il 15-20% più veloci. È anche una buona pratica, perché puoi dire dalla firma del metodo che chiamare il metodo non può alterare lo stato dell'oggetto.

http://developer.android.com/training/articles/perf-tips.html#PreferStatic


"che chiamare il metodo non può alterare lo stato dell'oggetto" - è incredibilmente fuorviante. Non so voi, ma certamente considero gli attributi statici di una classe come parte dello stato di un oggetto.
Adam Parkin

0

Bene, la documentazione di Eclipse dice sull'avvertimento in questione:

Il metodo può essere statico

Se abilitato, il compilatore emetterà un errore o un avviso per i metodi privati ​​o finali e che fanno riferimento solo ai membri statici

Penso che più o meno dice tutto. Se il metodo è privato e finale e si riferisce solo a membri statici, il metodo in questione potrebbe anche essere dichiarato statico e, in questo modo, rendere evidente che intendiamo solo accedere a contenuto statico da esso.

Onestamente non credo che ci sia nessun altro motivo misterioso dietro.


0

Mi mancavano alcuni numeri per le differenze di velocità. Quindi ho provato a confrontarli che si è rivelato non così facile: il ciclo Java diventa più lento dopo alcune esecuzioni / colpa di JIT?

Finalmente ho usato Caliper ei risultati sono gli stessi dell'esecuzione manuale dei miei test:

Non c'è differenza misurabile per chiamate statiche / dinamiche. Almeno non per Linux / AMD64 / Java7.

I risultati di Caliper si trovano qui: https://microbenchmarks.appspot.com/runs/1426eac9-36ca-48f0-980f-0106af064e8f#r:scenario.benchmarkSpec.methodName,scenario.vmSpec.options.CMSLargeCoalSurplusPercent,scenario.vmSpec. CMSLargeSplitSurplusPercent, scenario.vmSpec.options.CMSSmallCoalSurplusPercent, scenario.vmSpec.options.CMSSmallSplitSurplusPercent, scenario.vmSpec.options.FLSLargestBlockCoalesceProximity, scenario.vmSpec.

e i miei risultati sono:

Static: 352 ms
Dynamic: 353 ms
Static: 348 ms
Dynamic: 349 ms
Static: 349 ms
Dynamic: 348 ms
Static: 349 ms
Dynamic: 344 ms

La classe Caliper Test era:

public class TestPerfomanceOfStaticMethodsCaliper extends Benchmark {

    public static void main( String [] args ){

        CaliperMain.main( TestPerfomanceOfStaticMethodsCaliper.class, args );
    }

    public int timeAddDynamic( long reps ){
        int r=0;
        for( int i = 0; i < reps; i++ ) {
            r |= addDynamic( 1, i );
        }
        return r;
    }

    public int timeAddStatic( long reps ){
        int r=0;
        for( int i = 0; i < reps; i++ ) {
            r |= addStatic( 1, i );
        }
        return r;
    }

    public int addDynamic( int a, int b ){

        return a+b;
    }

    private static int addStatic( int a, int b ){

        return a+b;
    }

}

E la mia lezione di prova era:

public class TestPerformanceOfStaticVsDynamicCalls {

    private static final int RUNS = 1_000_000_000;

    public static void main( String [] args ) throws Exception{

        new TestPerformanceOfStaticVsDynamicCalls().run();
    }

    private void run(){

        int r=0;
        long start, end;

        for( int loop = 0; loop<10; loop++ ){

            // Benchmark

            start = System.currentTimeMillis();
            for( int i = 0; i < RUNS; i++ ) {
                r += addStatic( 1, i );
            }
            end = System.currentTimeMillis();
            System.out.println( "Static: " + ( end - start ) + " ms" );

            start = System.currentTimeMillis();
            for( int i = 0; i < RUNS; i++ ) {
                r += addDynamic( 1, i );
            }
            end = System.currentTimeMillis();
            System.out.println( "Dynamic: " + ( end - start ) + " ms" );

            // Do something with r to keep compiler happy
            System.out.println( r );

        }

    }

    private int addDynamic( int a, int b ){

        return a+b;
    }

    private static int addStatic( int a, int b ){

        return a+b;
    }

}

Sarebbe interessante vedere i risultati su vari dispositivi e versioni Android
Blundell

Sì. Pensavo che fossi interessato :) Ma dal momento che stai costruendo software Android e probabilmente hai un dispositivo Android collegato alla tua stazione di sviluppo, ti suggerisco di scegliere il codice, eseguirlo e condividere i risultati?
Scheintod

-2

I metodi che puoi dichiarare come statici sono quelli che non richiedono la creazione di istanze, come

public class MyClass
{
    public static string InvertText(string text)
    {
        return text.Invert();
    }
}

Che puoi quindi in cambio chiamare in qualsiasi altra classe senza istanziare quella classe.

public class MyClassTwo
{
    public void DoSomething()
    {
        var text = "hello world";
        Console.Write(MyClass.InvertText(text));
    }
}

... Ma è qualcosa che probabilmente già conosci. Non ti dà alcun vantaggio reale di per sé, oltre a rendere più chiaro che il metodo non utilizza variabili di istanza.

In altre parole, puoi semplicemente spegnerlo completamente. Se sai che non utilizzerai mai un metodo in altre classi (nel qual caso dovrebbe essere solo privato), non è affatto necessario che sia statico.


1
Qual è quella lingua? L'esempio viene compilato? Niente è statico qui.
Piotr Perak

In effetti, sembra che abbia dimenticato "statico" dal metodo InvertText. Ed è un esempio basato su c #
NeroS
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.