Was bringt es, eine Methode als statisch zu deklarieren?

94

Ich habe kürzlich meine Warnungen in Eclipse durchgesehen und bin auf diese gestoßen:

statische Warnung

Es wird eine Compiler-Warnung ausgegeben, wenn die Methode als statisch deklariert werden kann.

[Bearbeiten] Genaues Zitat in der Eclipse-Hilfe, mit Schwerpunkt auf privat und endgültig:

Wenn diese Option aktiviert ist, gibt der Compiler einen Fehler oder eine Warnung für Methoden aus, die privat oder endgültig sind und sich nur auf statische Mitglieder beziehen.

Ja, ich weiß, dass ich es ausschalten kann, aber ich möchte wissen, warum ich es einschalte ?

Warum wäre es gut, jede mögliche Methode als statisch zu deklarieren?

Wird dies Leistungsvorteile bringen? (in einer mobilen Domain)

Wenn Sie auf eine Methode als statisch hinweisen, zeigt dies vermutlich, dass Sie keine Instanzvariablen verwenden und daher in eine Utils-Stilklasse verschoben werden könnten.

Sollte ich am Ende des Tages einfach "Ignorieren" deaktivieren oder die über 100 Warnungen korrigieren, die es mir gegeben hat?

Denken Sie, dass dies nur zusätzliche Schlüsselwörter sind, die den Code verschmutzen , da der Compiler diese Methoden sowieso nur einbindet? (So ​​wie Sie nicht jede Variable deklarieren, die Sie finalisieren können, aber Sie könnten ).

Blundell
quelle
Unsicher, aber dies könnte einfach als Programmierhilfe angesehen werden. Warnungen sind lediglich Hinweise auf zu betrachtende Dinge.
James P.
1
Ich bin gespannt, welche Funktionalität diese Methoden bieten. Vielleicht wurde etwas nicht richtig gemacht, wenn es so viele davon gibt.
ArjunShankar
Siehe auch
Brian Rasmussen

Antworten:

132

Wann immer Sie eine Methode schreiben, erfüllen Sie einen Vertrag in einem bestimmten Umfang. Je enger der Bereich ist, desto geringer ist die Wahrscheinlichkeit, dass Sie einen Fehler schreiben.

Wenn eine Methode statisch ist, können Sie nicht auf nicht statische Mitglieder zugreifen. Daher ist Ihr Anwendungsbereich enger. Wenn Sie also (auch in Unterklassen) keine nicht statischen Mitglieder benötigen und niemals benötigen , um Ihren Vertrag zu erfüllen, warum sollten Sie Ihrer Methode Zugriff auf diese Felder gewähren? Wenn Sie staticin diesem Fall die Methode deklarieren, überprüft der Compiler, ob Sie keine Mitglieder verwenden, die Sie nicht verwenden möchten.

Darüber hinaus hilft es den Lesern beim Lesen Ihres Codes, die Art des Vertrags zu verstehen.

Aus diesem Grund wird es als gut angesehen, eine Methode zu deklarieren, staticwenn tatsächlich ein statischer Vertrag implementiert wird.

In einigen Fällen bedeutet Ihre Methode nur etwas relativ zu einer Instanz Ihrer Klasse, und es kommt vor, dass ihre Implementierung tatsächlich kein nicht statisches Feld oder keine nicht statische Instanz verwendet. In solchen Fällen würden Sie die Methode nicht markieren static.

Beispiele dafür, wo Sie das staticSchlüsselwort nicht verwenden würden :

  • Ein Erweiterungs-Hook, der nichts tut (aber etwas mit Instanzdaten in einer Unterklasse tun könnte)
  • Ein sehr einfaches Standardverhalten, das in einer Unterklasse anpassbar sein soll.
  • Implementierung des Ereignishandlers: Die Implementierung hängt von der Klasse des Ereignishandlers ab, verwendet jedoch keine Eigenschaft der Ereignisbehandlungsinstanz.
Samuel Rossille
quelle
1
+1 Es geht darum, die Möglichkeiten zu minimieren, sich in den Fuß zu schießen, und zu reduzieren, wie viel Sie wissen müssen, um eine Methode zu verstehen.
Peter Lawrey
7
Außerdem müssen Sie nicht mit dem Erwerb einer Instanz herumspielen, um eine unabhängige, in sich geschlossene Funktion aufzurufen.
Marko Topolnik
1
In anderen Welten sollte jede Methode, die keine Instanzvariablen verwendet, als statisch deklariert werden. Ich habe immer gedacht, dass Methoden nur dann statisch sein sollten, wenn dies wünschenswert ist (wie Utilility-Methoden).
Petr Mensik
18
@PetrMensik Wenn eine privateMethode als statisch deklariert werden könnte, sollte dies fast immer der Fall sein. Für jede andere Zugriffsebene sind andere Faktoren zu berücksichtigen, z. B. der dynamische Versand.
Marko Topolnik
1
@JamesPoulson Ich meine den dynamischen Methodenversand, das, was passiert, wenn object.method()man die aufzurufende Methode auswählt.
Marko Topolnik
15

Hier gibt es kein Konzept zur Optimierung.

Eine staticMethode liegt darin, staticdass Sie explizit deklarieren, dass die Methode von keiner Instanz der einschließenden Klasse abhängig ist, nur weil dies nicht erforderlich ist. Damit diese Eclipse-Warnung, wie in der Dokumentation angegeben:

Wenn diese Option aktiviert ist, gibt der Compiler einen Fehler oder eine Warnung für Methoden aus, die privat oder endgültig sind und sich nur auf statische Mitglieder beziehen.

Wenn Sie keine Instanzvariable benötigen und Ihre Methode privat (kann nicht von außen aufgerufen werden) oder final (kann nicht überschrieben werden) ist, gibt es keinen Grund, eine normale Methode anstelle einer statischen Methode zu verwenden. Eine statische Methode ist von Natur aus sicherer, nur weil Sie weniger Dinge damit tun dürfen (sie benötigt keine Instanz, Sie haben kein implizites thisObjekt).

Jack
quelle
7

Ich habe keine Informationen über die Leistung, ich nehme an, es ist höchstens geringfügig besser, da der Code keinen dynamischen Versand basierend auf dem Typ durchführen muss.

Ein viel stärkeres Argument gegen das Refactoring in statische Methoden ist jedoch, dass die derzeitige Verwendung von statischen Methoden als schlechte Praxis angesehen wird. Statische Methoden / Variablen lassen sich nicht gut in eine objektorientierte Sprache integrieren und sind auch schwer richtig zu testen. Dies ist der Grund, warum einige neuere Sprachen ganz auf das Konzept statischer Methoden / Variablen verzichten oder versuchen, es auf eine Weise in die Sprache zu verinnerlichen, die mit OO besser funktioniert (z. B. Objekte in Scala).

Meistens benötigen Sie statische Methoden, um Funktionen zu implementieren, die nur Parameter als Eingabe verwenden und eine Ausgabe damit erzeugen (z. B. Dienstprogramm- / Hilfsfunktionen). In modernen Sprachen gibt es ein erstklassiges Funktionskonzept, das dies ermöglicht, also statisch wird nicht gebraucht. In Java 8 sind Lambda-Ausdrücke integriert, daher bewegen wir uns bereits in diese Richtung.

Istvan Devai
quelle
7
Statische Methoden sind trivial zu testen (solange sie in sich geschlossen sind - insbesondere reine Funktionen, die das Hauptziel einer statischen Methode sind). Das OO-Konzept hat in diesem Fall nichts zu bieten. Auch das Konzept einer erstklassigen Funktion hat, wenn überhaupt, sehr wenig mit dem Konzept einer statischen Methode zu tun. Wenn Sie jemals eine erstklassige Funktion benötigen, die die Arbeit einer vorhandenen statischen Methode erledigt, müssen nur wenige Zeichen implementiert werden.
Marko Topolnik
5
Die Bemerkung zur Testbarkeit war wahrscheinlich nicht direkt auf die statische Methode ausgerichtet, aber statische Methoden sind viel schwieriger zu verspotten, so dass sie das Testen von Methoden erschweren, die statische Methoden aufrufen.
Buhb
2
Statische Methoden sind leicht zu verspotten, wenn Sie ein leistungsstarkes Verspottungsframework wie JMockit , PowerMock oder Groovy verwenden .
Jeff Olson
Dies sind private staticMethoden, so dass Sie sie nicht verspotten müssen
Blundell
3

1. DeklarationsmethodestaticDies bietet einen leichten Leistungsvorteil. Was jedoch nützlicher ist, ist die Verwendung, ohne dass eine Objektinstanz zur Hand ist (denken Sie beispielsweise an die Factory-Methode oder das Abrufen eines Singletons). Es dient auch dem dokumentatorischen Zweck, die Art der Methode zu erläutern. Dieser Dokumentationszweck sollte nicht ignoriert werden, da er den Lesern des Codes und den Benutzern der API einen unmittelbaren Hinweis auf die Art der Methode gibt und auch dem ursprünglichen Programmierer als Denkwerkzeug dient - es hilft, die beabsichtigte Bedeutung explizit anzugeben Sie denken auch klar und produzieren Code mit besserer Qualität (ich denke, basierend auf meiner persönlichen Erfahrung, aber die Leute sind anders). Zum Beispiel ist es logisch und daher wünschenswert, zwischen Methoden zu unterscheiden, die mit einem Typ arbeiten, und Methoden, die auf eine Instanz des Typs wirken (wie von ausgeführt)Jon Skeet in seinem Kommentar zu einer C # -Frage ).

Ein weiterer Anwendungsfall für staticMethoden ist die Nachahmung der prozeduralen Programmierschnittstelle. Denken Sie an die java.lang.System.println()Klasse und die darin enthaltenen Methoden und Attribute. Die Klasse java.lang.Systemwird eher als Gruppierungsnamenraum als als instanziierbares Objekt verwendet.

2. Wie kann Eclipse (oder eine andere programmierte oder andere Art von - biokomposierbarer oder nicht biokomposierbarer - Entität) sicher wissen, welche Methode als statisch deklariert werden kann? Selbst wenn eine Basisklasse nicht auf Instanzvariablen zugreift oder nicht statische Methoden aufruft, können sich die Dinge durch den Vererbungsmechanismus ändern. Nur wenn die Methode nicht durch das Erben der Unterklasse überschrieben werden kann, können wir mit 100% iger Sicherheit behaupten, dass die Methode wirklich deklariert werden kann static. Das Überschreiben einer Methode ist genau in beiden Fällen unmöglich

  1. private (Keine Unterklasse kann es direkt verwenden und weiß im Prinzip nicht einmal davon) oder
  2. final (Selbst wenn die Unterklasse darauf zugreifen kann, gibt es keine Möglichkeit, die Methode so zu ändern, dass sie auf Instanzdaten oder -funktionen verweist.)

Daher die Logik der Eclipse-Option.

3. Das Originalposter fragt außerdem: " Wenn Sie auf eine Methode als statisch hinweisen, zeigt dies vermutlich, dass Sie keine Instanzvariablen verwenden und daher in eine Utils-Stilklasse verschoben werden könnten. " Dies ist ein sehr guter Punkt. Manchmal wird diese Art von Designänderung durch die Warnung angezeigt.

Es ist eine sehr nützliche Option, die ich persönlich aktivieren würde, wenn ich Eclipse verwenden und in Java programmieren würde.

FooF
quelle
1

Siehe Samuels Antwort, wie sich der Umfang der Methode ändert. Ich denke, dies ist der Hauptaspekt, um eine Methode statisch zu machen.

Sie haben auch nach der Leistung gefragt:

Es kann zu einem geringfügigen Leistungsgewinn kommen, da für einen Aufruf einer statischen Methode die implizite Referenz "this" nicht als Parameter benötigt wird.

Diese Auswirkungen auf die Leistung sind jedoch sehr gering. Daher dreht sich alles um den Umfang.

Schwarz
quelle
1

Aus den Android-Leistungsrichtlinien:

Statisch gegenüber virtuell bevorzugen Wenn Sie nicht auf die Felder eines Objekts zugreifen müssen, machen Sie Ihre Methode statisch. Aufrufe werden etwa 15% -20% schneller sein. Dies ist auch eine gute Vorgehensweise, da Sie anhand der Methodensignatur erkennen können, dass der Aufruf der Methode den Status des Objekts nicht ändern kann.

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

Blundell
quelle
"Das Aufrufen der Methode kann den Status des Objekts nicht ändern" - ist unglaublich irreführend. Ich weiß nichts über dich, aber ich betrachte statische Attribute einer Klasse auf jeden Fall als Teil des Zustands eines Objekts.
Adam Parkin
0

Nun, die Eclipse-Dokumentation sagt über die fragliche Warnung:

Methode kann statisch sein

Wenn diese Option aktiviert ist, gibt der Compiler einen Fehler oder eine Warnung für Methoden aus, die privat oder endgültig sind und sich nur auf statische Mitglieder beziehen

Ich denke, es sagt so ziemlich alles. Wenn die Methode privat und endgültig ist und sich nur auf statische Elemente bezieht, kann die betreffende Methode genauso gut als statisch deklariert werden. Dadurch wird deutlich, dass wir nur von dort aus auf statische Inhalte zugreifen möchten.

Ich glaube ehrlich gesagt nicht, dass es einen anderen mysteriösen Grund dafür gibt.

Edwin Dalorzo
quelle
0

Mir fehlten einige Zahlen für die Geschwindigkeitsunterschiede. Also habe ich versucht, sie zu vergleichen, was sich als nicht so einfach herausstellte: Die Java-Schleife wird nach einigen Läufen / JIT-Fehlern langsamer?

Ich habe endlich Caliper verwendet und die Ergebnisse sind die gleichen wie beim Ausführen meiner Tests von Hand:

Es gibt keinen messbaren Unterschied für statische / dynamische Anrufe. Zumindest nicht für Linux / AMD64 / Java7.

Die Caliper-Ergebnisse finden Sie hier: https://microbenchmarks.appspot.com/runs/1426eac9-36ca-48f0-980f-0106af064e8f#r:scenario.benchmarkSpec.methodName,scenario.vmSpec.options.CMSLargeCoalSurplusPercent.scen. CMSLargeSplitSurplusPercent, Szenario.vmSpec.options.CMSSmallCoalSurplusPercent, Szenario.vmSpec.options.CMSSmallSplitSurplusPercent, Szenario.vmSpec.options.FLSLargestBlockCoalesceProximity.Sp.cm

und meine eigenen Ergebnisse sind:

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

Die Caliper Test Klasse war:

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

}

Und meine eigene Testklasse war:

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

}
Scheintod
quelle
wäre interessant, die Ergebnisse auf verschiedenen Android-Geräten und Versionen zu sehen
Blundell
Ja. Ich dachte, dass Sie interessiert wären :) Aber da Sie Android-Software erstellen und wahrscheinlich ein Android-Gerät an Ihre Entwicklungsstation angeschlossen haben, schlage ich vor, dass Sie einfach den Code auswählen, ausführen und die Ergebnisse teilen?
Scheintod
-2

Die Methoden, die Sie als statisch deklarieren können, erfordern keine Instanziierung, z

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

Was Sie dann im Gegenzug in jeder anderen Klasse aufrufen können, ohne diese Klasse zu instanziieren.

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

... Aber das wissen Sie wahrscheinlich schon. Es bietet Ihnen an sich keine wirklichen Vorteile, außer dass Sie klarer machen, dass die Methode keine Instanzvariablen verwendet.

Mit anderen Worten, Sie können es am sichersten einfach vollständig ausschalten. Wenn Sie wissen, dass Sie eine Methode niemals in anderen Klassen verwenden werden (in diesem Fall sollte sie nur privat sein), muss sie überhaupt nicht statisch sein.

NeroS
quelle
1
Was ist das für eine Sprache? Kompiliert dieses Beispiel? Hier ist nichts statisch.
Piotr Perak
In der Tat habe ich anscheinend 'statisch' von der InvertText-Methode vergessen. Und es ist ein Beispiel basierend auf c #
NeroS