Jakie są korzyści z zadeklarowania metody jako statycznej

94

Ostatnio przeglądałem swoje ostrzeżenia w Eclipse i natknąłem się na to:

ostrzeżenie statyczne

Wyśle ostrzeżenie kompilatora, jeśli metodę można zadeklarować jako statyczną.

[edytuj] Dokładny cytat w pomocy Eclipse, z naciskiem na prywatne i końcowe:

Po włączeniu kompilator wyświetli błąd lub ostrzeżenie dla metod, które są prywatne lub ostateczne i które odnoszą się tylko do statycznych elementów członkowskich.

Tak, wiem, że mogę to wyłączyć, ale chcę poznać powód jego włączenia?

Dlaczego warto zadeklarować każdą możliwą metodę jako statyczną?

Czy da to jakieś korzyści związane z wydajnością? (w domenie mobilnej)

Wskazanie metody jako statycznej, jak przypuszczam, pokazuje, że nie używasz żadnych zmiennych instancji, dlatego można je przenieść do klasy stylu narzędzi?

Na koniec dnia powinienem po prostu wyłączyć to „ignoruj”, czy też powinienem naprawić ponad 100 ostrzeżeń, które mi dało?

Czy uważasz, że to tylko dodatkowe słowa kluczowe, które brudzą kod, ponieważ kompilator i tak po prostu wstawi te metody? (tak jakbyś nie deklarował każdej zmiennej, którą możesz sfinalizować, ale możesz ).

Blundell
źródło
Nie mam pewności, ale można to po prostu uznać za pomoc w programowaniu. Ostrzeżenia są po prostu wskazówkami, na które należy zwrócić uwagę.
James P.
1
Ciekawi mnie, jaką funkcjonalność spełniają te metody. Może coś nie zostało zrobione dobrze, jeśli jest ich tak wiele.
ArjunShankar
Związane z: stackoverflow.com/q/790281
Brian Rasmussen

Odpowiedzi:

133

Za każdym razem, gdy piszesz metodę, realizujesz kontrakt w danym zakresie. Im węższy zakres, tym mniejsza szansa, że ​​napiszesz błąd.

Gdy metoda jest statyczna, nie można uzyskać dostępu do niestatycznych elementów członkowskich; stąd twój zakres jest węższy. Jeśli więc nie potrzebujesz i nigdy nie będziesz potrzebować (nawet w podklasach) niestatycznych elementów członkowskich do wypełnienia kontraktu, po co dawać dostęp do tych pól swojej metodzie? Zadeklarowanie metody staticw tym przypadku pozwoli kompilatorowi sprawdzić, czy nie używasz elementów członkowskich, których nie zamierzasz używać.

Co więcej, pomoże to osobom czytającym Twój kod zrozumieć charakter umowy.

Dlatego dobrze jest zadeklarować metodę, staticgdy faktycznie implementuje kontrakt statyczny.

W niektórych przypadkach twoja metoda oznacza tylko coś odnoszącego się do instancji twojej klasy i zdarza się, że jej implementacja w rzeczywistości nie używa żadnego niestatycznego pola lub instancji. W takich przypadkach nie oznaczałbyś metody static.

Przykłady sytuacji, w których nie można użyć staticsłowa kluczowego:

  • Hak rozszerzenia, który nic nie robi (ale mógłby zrobić coś z danymi instancji w podklasie)
  • Bardzo proste zachowanie domyślne, które można dostosowywać w podklasie.
  • Implementacja procedury obsługi zdarzeń: implementacja będzie się różnić w zależności od klasy procedury obsługi zdarzeń, ale nie będzie używać żadnej właściwości instancji procedury obsługi zdarzeń.
Samuel Rossille
źródło
1
+1 Chodzi o zminimalizowanie okazji do strzelenia sobie w stopę i zmniejszenie ilości wiedzy potrzebnej do zrozumienia metody.
Peter Lawrey,
7
Ponadto nie musisz majstrować przy pozyskiwaniu instancji tylko po to, aby wywołać niezależną, samodzielną funkcję.
Marko Topolnik
1
Więc w innych światach każda metoda, która nie używa zmiennych instancji, powinna być zadeklarowana jako statyczna? Zawsze uważałem, że metody powinny być statyczne tylko wtedy, gdy jest to pożądane (jak metody użyteczności).
Petr Mensik
18
@PetrMensik Jeśli privatemetoda może być zadeklarowana jako statyczna, to prawie zawsze tak powinno być. W przypadku każdego innego poziomu dostępu należy wziąć pod uwagę inne czynniki, takie jak dynamiczna wysyłka.
Marko Topolnik
1
@JamesPoulson Mam na myśli dynamiczne wysyłanie metod, co dzieje się podczas egzekwowania object.method()wyboru metody do wywołania.
Marko Topolnik
15

Nie ma tutaj koncepcji z optymalizacją.

staticMetoda static, ponieważ wyraźnie oświadczyć, że metoda nie polega na dowolnej instancji klasy załączając tylko dlatego, że nie ma takiej potrzeby. Więc to ostrzeżenie Eclipse, jak podano w dokumentacji:

Po włączeniu kompilator wyświetli błąd lub ostrzeżenie dla metod, które są prywatne lub ostateczne i które odnoszą się tylko do statycznych elementów członkowskich.

Jeśli nie potrzebujesz żadnej zmiennej instancji, a Twoja metoda jest prywatna (nie można jej wywołać z zewnątrz) lub ostateczna (nie można jej nadpisać), nie ma powodu, aby była to zwykła metoda zamiast metody statycznej. Metoda statyczna jest z natury bezpieczniejsza, nawet dlatego, że możesz z nią zrobić mniej rzeczy (nie wymaga ona żadnej instancji, nie masz żadnego ukrytego thisobiektu).

Jacek
źródło
7

Nie mam informacji na temat wydajności, przypuszczam, że jest co najwyżej nieznacznie lepsza, ponieważ kod nie musi wykonywać dynamicznego wysyłania na podstawie typu.

Jednak znacznie silniejszym argumentem przeciwko refaktoryzacji do metod statycznych jest to, że obecnie używanie statyczne jest uważane za złą praktykę. Metody / zmienne statyczne nie integrują się dobrze z językiem zorientowanym obiektowo, a także są trudne do poprawnego przetestowania. To jest powód, dla którego niektóre nowsze języki całkowicie rezygnują z koncepcji statycznych metod / zmiennych lub próbują internalizować je w języku w sposób, który lepiej współgra z obiektami obiektowymi (np. Obiekty w Scali).

W większości przypadków do implementacji funkcji, które używają parametrów jako danych wejściowych i generują dane wyjściowe (np. Funkcje narzędziowe / pomocnicze), potrzebne są statyczne metody, które pozwalają na to w językach współczesnych. nie jest potrzebne. Java 8 będzie miała zintegrowane wyrażenia lambda, więc już idziemy w tym kierunku.

Istvan Devai
źródło
7
Metody statyczne są łatwe do przetestowania (o ile są niezależne - zwłaszcza czyste funkcje, które są głównym celem dla metody statycznej). W tym przypadku koncepcja OO nie ma nic do zaoferowania. Ponadto koncepcja funkcji pierwszej klasy ma bardzo niewiele wspólnego z koncepcją metody statycznej. Jeśli kiedykolwiek potrzebujesz funkcji pierwszej klasy, która wykonuje pracę istniejącej metody statycznej, jej zaimplementowanie jest kwestią kilku znaków.
Marko Topolnik
5
Uwaga dotycząca testowalności prawdopodobnie nie była ukierunkowana bezpośrednio na metodę statyczną, ale metody statyczne są znacznie trudniejsze do podrobienia, więc komplikują testowanie metod, które wywołują metody statyczne.
Buhb
2
Metody statyczne są łatwe do mock jeśli używasz potężne ramy szyderczy jak JMockit , PowerMock lub Groovy .
Jeff Olson,
To są private staticmetody, więc nie będziesz musiał z nich kpić
Blundell,
3

1. Metoda deklaracjistaticdaje niewielką korzyść wydajnościową, ale co jest bardziej przydatne, pozwala na użycie go bez posiadania instancji obiektu pod ręką (pomyślmy na przykład o metodzie fabrycznej lub uzyskaniu singletona). Służy również dokumentacyjnemu określeniu natury metody. Ten cel dokumentacyjny nie powinien być ignorowany, ponieważ daje natychmiastową wskazówkę o naturze metody czytelnikom kodu i użytkownikom API, a także służy jako narzędzie myślenia dla oryginalnego programisty - wyraźne określenie zamierzonego znaczenia pomaga Ty też myślisz trzeźwo i tworzysz kod lepszej jakości (myślę na podstawie własnego doświadczenia, ale ludzie są różni). Na przykład jest logiczne i dlatego pożądane jest rozróżnienie między metodami operującymi na typie i metodami działającymi na instancji typu (jak wskazano wJon Skeet w komentarzu do pytania w języku C # ).

Jeszcze innym przypadkiem użycia staticmetod jest naśladowanie interfejsu programowania proceduralnego. Myśleć ojava.lang.System.println() klasie oraz zawartych w niej metodach i atrybutach. Klasa java.lang.Systemjest używana jako przestrzeń nazw grupowania, a nie obiekt, który można utworzyć.

2. jaki sposób Eclipse (lub jakikolwiek inny zaprogramowany lub inny rodzaj - biokompozowalny lub niebiokompozowalny - podmiot) może wiedzieć na pewno, która metoda może być zadeklarowana jako statyczna? Nawet jeśli klasa bazowa nie uzyskuje dostępu do zmiennych instancji lub nie wywołuje metod niestatycznych, mechanizm dziedziczenia może to zmienić. Tylko wtedy, gdy metody nie można przesłonić przez dziedziczenie podklasy, możemy stwierdzić ze 100% pewnością, że metoda rzeczywiście może zostać zadeklarowana static. Przesłonięcie metody jest niemożliwe dokładnie w dwóch przypadkach bytu

  1. private (żadna podklasa nie może go używać bezpośrednio i nawet w zasadzie o tym nie wie) lub
  2. final (nawet jeśli jest dostępna dla podklasy, nie ma możliwości zmiany metody odwoływania się do danych lub funkcji instancji).

Stąd logika opcji Eclipse.

3. Oryginalny plakat zawiera również pytanie: „ Wskazanie metody jako statycznej, jak przypuszczam, pokazuje, że nie używasz żadnych zmiennych instancji, dlatego można by je przenieść do klasy stylu narzędzi? ” To bardzo dobra uwaga. Czasami tego rodzaju zmiana projektu jest sygnalizowana ostrzeżeniem.

Jest to bardzo przydatna opcja, którą osobiście bym włączył, gdybym używał Eclipse i gdybym programował w Javie.

FooF
źródło
1

Zobacz odpowiedź Samuela na temat zmian zakresu metody. Wydaje mi się, że to jest główny aspekt statycznej metody.

Zapytałeś również o wydajność:

Może wystąpić niewielki wzrost wydajności, ponieważ wywołanie metody statycznej nie wymaga niejawnego odwołania „this” jako parametru.

Jednak ten wpływ na wydajność jest naprawdę niewielki. Dlatego chodzi o zakres.

czarny
źródło
1

Z wytycznych dotyczących wydajności systemu Android:

Preferuj statyczne zamiast wirtualne Jeśli nie potrzebujesz dostępu do pól obiektu, ustaw metodę jako statyczną. Inwokacje będą szybsze o około 15% -20%. Jest to również dobra praktyka, ponieważ po sygnaturze metody można stwierdzić, że wywołanie metody nie może zmienić stanu obiektu.

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

Blundell
źródło
„że wywołanie metody nie może zmienić stanu obiektu” - jest niesamowicie mylące. Nie wiem jak wy, ale z pewnością uważam statyczne atrybuty klasy za część stanu obiektu.
Adam Parkin
0

Cóż, dokumentacja Eclipse mówi o omawianym ostrzeżeniu:

Metoda może być statyczna

Po włączeniu kompilator wyświetli błąd lub ostrzeżenie dla metod, które są prywatne lub ostateczne i które odnoszą się tylko do statycznych elementów członkowskich

Myślę, że to mówi wszystko. Jeśli metoda jest prywatna i ostateczna i odwołuje się tylko do statycznych elementów członkowskich, dana metoda może być równie dobrze zadeklarowana jako statyczna, a przez to wyraźnie widać, że zamierzamy uzyskiwać z niej dostęp tylko do zawartości statycznej.

Szczerze mówiąc, nie sądzę, aby był za tym jakiś inny tajemniczy powód.

Edwin Dalorzo
źródło
0

Brakowało mi kilku liczb dotyczących różnic prędkości. Więc próbowałem je porównać, co okazało się nie takie proste: pętla Java działa wolniej po kilku uruchomieniach / błąd JIT?

W końcu użyłem Calipera i wyniki są takie same, jak podczas ręcznego testowania:

Nie ma mierzalnej różnicy w przypadku połączeń statycznych / dynamicznych. Przynajmniej nie dla Linux / AMD64 / Java7.

Wyniki suwmiarki są tutaj: https://microbenchmarks.appspot.com/runs/1426eac9-36ca-48f0-980f-0106af064e8f#r:scenario.benchmarkSpec.methodName,scenario.vmSpec.options.CMSLargeCoalSurplusPervcentm.scenario.scenario.benchmark. CMSLargeSplitSurplusPercent, scenario.vmSpec.options.CMSSmallCoalSurplusPercent, scenario.vmSpec.options.CMSSmallSplitSurplusPercent, scenario.vmSpec.options.FLSLargestBlockCoalesceProxtimity, scenario1.

a moje własne wyniki to:

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

Klasa testu suwmiarki to:

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

}

A moja własna klasa testowa to:

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
źródło
byłoby interesujące zobaczyć wyniki na różnych urządzeniach z Androidem i wersjach
Blundell
Tak. Pomyślałem, że byłbyś zainteresowany :) Ale skoro tworzysz oprogramowanie dla Androida i prawdopodobnie masz urządzenie z Androidem podłączone do Twojej stacji deweloperskiej, sugeruję po prostu wybrać kod, uruchomić go i udostępnić wyniki?
Scheintod
-2

Metody, które można zadeklarować jako statyczne, to te, które nie wymagają tworzenia instancji, na przykład

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

Które możesz potem w zamian zawołać w dowolnej innej klasie bez inicjowania tej klasy.

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

... Ale to jest coś, co prawdopodobnie już wiesz. Nie daje żadnych rzeczywistych korzyści jako takich, poza wyraźnym wyjaśnieniem, że metoda nie wykorzystuje żadnych zmiennych instancji.

Innymi słowy, najbezpieczniej jest po prostu całkowicie go wyłączyć. Jeśli wiesz, że nigdy nie użyjesz metody w innych klasach (w takim przypadku powinna być po prostu prywatna), wcale nie potrzebujesz, aby była statyczna.

NeroS
źródło
1
Co to za język? Czy ten przykład się kompiluje? Nic tu nie jest statyczne.
Piotr Perak
Rzeczywiście, wygląda na to, że zapomniałem „statycznego” z metody InvertText. I to jest przykład oparty na c #
NeroS,