Czy metody prywatnego pomocnika powinny być statyczne, jeśli mogą być statyczne


205

Let's say I have a class designed to be instantiated. I have several private "helper" methods inside the class that do not require access to any of the class members, and operate solely on their arguments, returning a result.

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

Is there any particular reason to specify computeOne and computeMore as static methods - or any particular reason not to?

It is certainly easiest to leave them as non-static, even though they could certainly be static without causing any problems.


3
Zobacz także, kiedy nie należy używać słowa kluczowego static w Javie: stackoverflow.com/questions/1766715/…
Mark Butler,

Odpowiedzi:


174

Wolę takie metody pomocnicze private static; co wyjaśni czytelnikowi, że nie będą modyfikować stanu obiektu. Moje IDE pokaże również kursywą wywołania metod statycznych, więc będę wiedział, że metoda jest statyczna bez szukania podpisu.


2
One of my favorite threads.I am using NetBeans and it shows static method calls with italic fonts.
skiabox

2
I normally declare those helper methods as protected static which makes them testable very easily in a test class in the same package.
James

2
@James or simply static (if we just want it for testing).
mrod

3
„Nie modyfikuje stanu obiektu” - doskonałe wyjaśnienie, jak odróżnić go od metody prywatnej.
HopeKing

110

Może to spowodować nieco mniejszy kod bajtowy, ponieważ metody statyczne nie uzyskają dostępu do this. Nie sądzę, żeby miało to jakąkolwiek różnicę w szybkości (a gdyby tak się stało, prawdopodobnie byłby zbyt mały, aby zrobić ogólną różnicę).

Uczyniłbym je statycznymi, ponieważ generalnie robię to, jeśli to w ogóle możliwe. Ale to tylko ja.


EDYCJA: Ta odpowiedź wciąż jest lekceważona, być może z powodu bezpodstawnego twierdzenia o rozmiarze kodu bajtowego. Więc faktycznie przeprowadzę 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);
    }
}

Kod bajtowy (pobrany za pomocą 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

}

Wywołanie metody statycznej wymaga dwóch bajtów (bajtów?): iconst_0(Dla argumentu) i invokestatic.
Wywołanie metody niestatycznej wymaga trzech: aload_1(jak TestBytecodeSizesądzę dla obiektu), iconst_0(dla argumentu) i invokespecial. (Zauważ, że gdyby nie były to metody prywatne, byłoby to invokevirtualzamiast tego invokespecial; patrz JLS §7.7 Metody wywoływania ).

Teraz, jak powiedziałem, nie oczekuję, że między tymi dwoma invokestaticrzeczami będzie jakaś wielka różnica, poza faktem, że wymaga o jeden mniej bajtekodu. invokestatici invokespecialoba powinny być nieco szybsze niż invokevirtual, ponieważ oba używają wiązania statycznego zamiast dynamicznego, ale nie mam pojęcia, czy jedno z nich jest szybsze od drugiego. Nie mogę też znaleźć żadnych dobrych referencji. Najbliżej mogę znaleźć ten artykuł JavaWorld z 1997 roku , który w zasadzie stanowi powtórzenie tego , co właśnie powiedziałem:

Najszybsze instrukcje najprawdopodobniej będą, invokespeciala invokestaticponieważ metody wywoływane przez te instrukcje są statycznie powiązane. Kiedy JVM rozpozna symboliczne odniesienie dla tych instrukcji i zastąpi je bezpośrednim odwołaniem, to bezpośrednie odniesienie prawdopodobnie będzie zawierało wskaźnik do rzeczywistych kodów bajtowych.

Ale wiele rzeczy zmieniło się od 1997 roku.

Podsumowując ... Chyba wciąż trzymam się tego, co powiedziałem wcześniej. Szybkość nie powinna być powodem do wybierania jednej z drugiej, ponieważ w najlepszym przypadku byłaby to mikrooptymalizacja.


wszystkie te negatywne głosy za, jak się wydaje, bardzo prostą odpowiedzią. +1 w interesie prawdy, sprawiedliwości, ...
Steve B.

Dzięki. (Chociaż mogłem go usunąć i dostać odznakę, gdy był w -3.: D)
Michael Myers

2
Kompilator JIT i tak je wbuduje i zoptymalizuje, więc ilość instrukcji kodu bajtowego ma niewiele wspólnego z jego szybkością.
Esko Luontola

3
Właśnie dlatego powiedziałem: „Nie sądzę, żeby miało to jakąkolwiek różnicę w prędkości (a gdyby tak było, prawdopodobnie byłby zbyt mały, aby zrobić ogólną różnicę”).
Michael Myers

7
Przed zaangażowaniem się w jakiekolwiek tego rodzaju mikrooptymalizacje należy rozważyć efekt kompilatora „gorących punktów”. Konkluzja: napisz kod dla czytelności dla ludzi i pozostaw optymalizację kompilatorowi
Ichthyo

18

Moją osobistą preferencją byłoby deklarowanie ich jako statycznych, ponieważ jest to wyraźna flaga, że ​​są bezpaństwowcami.


18

Odpowiedź brzmi ... to zależy.

Jeśli członek jest zmienną instancji specyficzną dla obiektu, z którym mamy do czynienia, to po co w ogóle przekazywać ją jako parametr?

Na przykład:

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: Mimo że przykład jest zły, zbyt wiele metod, które MOGĄ być statyczne, to nieprzyjemny zapach kodu. Nawiasem mówiąc, metody statyczne są w rzeczywistości funkcjami, wcale nie są OO. Mogą należeć jako metoda do innej klasy lub może brakować klasy, do której ta funkcja mogłaby należeć jako metoda.
Bill K

11

Jednym z powodów, dla których warto zadeklarować metody statycznego pomocnika, jest konieczność wywołania ich w konstruktorze klas „przed” thislub super. Na przykład:

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

Jest to nieco wymyślony przykład, ale najwyraźniej recoverIntnie może być w tym przypadku metodą instancji.


możesz wywołać metodę instancji w konstruktorze, ale jeśli delegujesz do innego konstruktora za pomocą super (...) lub tego (...), nie możesz wywołać metody instancji przed delegacją (ale statystyki są ok, jak
wskazałeś

10

Naprawdę nie mogę wymyślić wyraźnych korzyści dla prywatnej metody statycznej. To powiedziawszy, nie ma też szczególnych zalet, aby uczynić je niestatycznymi. Jest to głównie kwestia prezentacji: możesz ustawić je w sposób statyczny, aby wyraźnie podkreślić fakt, że nie zmieniają obiektu.

W przypadku metody z różnymi uprawnieniami dostępu sądzę, że istnieją dwa główne argumenty:

  • metody statyczne można wywoływać bez tworzenia instancji obiektu, co może być przydatne
  • metody statyczne nie mogą być dziedziczone, co może stanowić problem, jeśli potrzebujesz polimorfizmu (ale nie ma znaczenia dla metod prywatnych).

Poza tym różnica jest dość niewielka i mocno wątpię, że dodatkowa wartość tego wskaźnika przekazanego do metody instancji robi znaczącą różnicę.


4
Zaletą braku uczynienia go statycznym jest to, że ktoś może podklasować i zastąpić zachowanie metody.
Clint Miller

7
Clint to prywatna metoda, więc nie można zastąpić tej metody.
Bhushan Bhangale

Dokładnie, w przypadku metody prywatnej nie sądzę, żeby miało to duże znaczenie. W przypadku metod publicznych zgadzam się jednak z tym, co mówisz
Axelle Ziegler,

8

Prawidłowa odpowiedź to:

każda metoda, która nie pobiera żadnych informacji z pola i nie umieszcza żadnych informacji w polu, nie musi być metodą instancji. Każda metoda, która nie używa ani nie zmienia żadnych pól w swojej klasie lub obiekcie, może równie dobrze być metodą statyczną.


5

lub jakiś konkretny powód, aby nie [deklarować ich jako statycznych]?

Tak.

Zachowując je jako metody instancji, możesz później zapewnić inną implementację.

Może to zabrzmieć głupio (i faktycznie byłoby tak, gdyby te metody były używane tylko przez ciebie w programie 50-liniowym), ale w większych aplikacjach lub w bibliotekach używanych przez kogoś innego, możesz zdecydować się na lepszą implementację, ale nie chcę złamać istniejący kod.

Tworzysz więc podklasę i zwracasz ją w nowych wersjach, a ponieważ metody zostały zadeklarowane jako metody instancji, pozwalasz polimorfizmowi na wykonanie swojej pracy.

Dodatkowo możesz skorzystać z ustawienia konstruktora na prywatny i zapewnić statyczną metodę fabryczną z tego samego powodu.

Więc moim zaleceniem jest zachowanie ich jako metod instancji i unikanie w miarę możliwości statycznych.
Skorzystaj z dynamizmu, jaki zapewnia język.

Zobacz tutaj nieco pokrewny film: Jak zaprojektować dobry interfejs API i dlaczego ma to znaczenie

Chociaż nie jest to bezpośrednio związane z dyskusją na temat metody „static vs. instance”, dotyczy kilku interesujących punktów w projektowaniu API.


1
Zgadzać się w zupełności. Z tego właśnie powodu zwykle staram się całkowicie unikać statyki i szeregowców. Myślę, że większość innych odpowiedzi nie trafiła w sedno.
Clint Miller,

22
Mówimy o prywatnych pomocnikach, co oznacza, że ​​nie są oni częścią publicznego API klasy. Oznacza to, że nic nie stoi na przeszkodzie, aby później wprowadzić inną implementację, uczynić ją niestatyczną lub dokonać innych zmian.
Jonik

Aaaahmm ... to dobra uwaga Jonik. Jednak stworzenie dobrego nawyku powinno być wystarczającym powodem (oczywiście subiektywnie)
OscarRyz

2
Całkowicie się nie zgadzam. Nie ma dobrego powodu, aby chcieć zmienić metodę prywatną bez zmiany klasy, w której jest zdefiniowana. Jedynym sposobem na zrobienie tego, co sugerujesz, jest manipulacja kodem bajtowym.
Peter Lawrey

2
To, co sugerujesz, może mieć sens, gdyby metoda nie była prywatna, ale nawet wtedy sugerowałbym projektowanie oparte na potrzebnym prądzie, a nie projektowanie oparte na wyobrażonej potrzebie.
Peter Lawrey,

5

Jednym z problemów związanych z posiadaniem metod statycznych jest to, że może to utrudnić użycie obiektu w testach jednostkowych . Mockito nie może tworzyć próbnych metod dla metod statycznych i nie można utworzyć implementacji tej podklasy.


2
Nie powinieneś pisać testów dla metod prywatnych. Zobacz tutaj . Nie ma potrzeby testowania prywatnej metody, czy jest statyczna czy nie.
BW

@BW To bardzo subiektywna sprawa. Istnieje wiele ważnych powodów, aby przetestować metodę prywatną.
ruohola,

4

Jeśli metoda jest w zasadzie tylko podprogramem, który nigdy nie będzie w przewidywalny sposób wykorzystywać informacji o stanie, zadeklaruj ją jako statyczną.

Pozwala to na użycie go w innych metodach statycznych lub podczas inicjalizacji klas, tj .:

public class Example {
   //...

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

   //...
}

3

Moje preferencje w takich przypadkach jest, aby computeOnei computeMoremetod statycznych. Powód: enkapsulacja. Im mniej kodu ma dostęp do implementacji twojej klasy, tym lepiej.

Na przykład dajesz, stwierdzają, że computeOnei computeMorenie powinny mieć dostęp do wewnętrznych części klasy, dlaczego więc dać szansę na opiekunów klasy mieszać się z wewnętrznych.


3

Chciałbym wyjaśnić kilka rzeczy, które inni mówili, ponieważ podają nieprawidłowe informacje.

Po pierwsze, ponieważ metody są prywatne, nawet jeśli zadeklarujesz je jako statyczne, nie będziesz mógł uzyskać do nich dostępu poza tą klasą. Po drugie, są prywatne, więc nie można nawet przesłonić podklasy, więc statyczna lub niestatyczna nie robi żadnej różnicy. Po trzecie, od konstruktora klasy można również wywołać niestatystyczną metodę prywatną, nie musi ona być statyczna.

Teraz pojawia się pytanie, czy prywatną metodę pomocniczą należy zdefiniować jako statyczną czy niestatyczną. Pójdę z odpowiedzią Steve'a, ponieważ oznaczenie prywatnej metody statycznej pokazuje, że ta metoda jest bezstanowa, ponieważ przestrzegam tej zasady również podczas kodowania.


ale są miejsca, w których można wywołać tylko metodę statyczną, jak w moim przykładzie i w przykładzie Chrisa Marshalla
Kip

Tak Kip twoje i Chris odpowiedzi są poprawne. Nie skomentowałem tych, ponieważ są to poprawne odpowiedzi. Mówiłem tylko o złych odpowiedziach.
Bhushan Bhangale

3

Z doświadczenia powiedziałbym, że takie prywatne metody są na ogół dość uniwersalne i wielokrotnego użytku.

Myślę, że pierwszą rzeczą do zrobienia jest zadanie pytania, czy metoda może być przydatna poza bieżącym kontekstem klasowym. Jeśli tak, zrobiłbym dokładnie to, co sugeruje Każdy, i wyodrębniłbym tę metodę jako statyczną do jakiejś klasy utils, gdzie ktoś, miejmy nadzieję, sprawdzi przed wdrożeniem nowej metody dokładnie to samo.

Takie prywatne metody powszechnego użytku są źródłem dużej części duplikacji kodu w projekcie, ponieważ każdy programista samodzielnie je zmienia na nowo w miejscu, w którym musi go użyć. Tak więc centralizacja takich metod jest dobrym rozwiązaniem.


2

Pytanie statyczne / niestatyczne sprowadza się do „czy naprawdę muszę użyć obiektu tej klasy”?

Czy przekazujesz obiekt między różnymi metodami? Czy obiekt zawiera informacje przydatne poza kontekstem metod statycznych? Czy istnieje jakikolwiek powód, aby nie definiować metod w obie strony, jeśli będziesz ich używać w obie strony?

Jeśli jesteś w tym dylemacie, wydaje mi się, że masz wszystkie dane wymagane dla metody unoszące się w kodzie poza obiektem. Czy to jest to, czego chcesz? Czy łatwiej byłoby zawsze zbierać te dane za każdym razem za każdym razem? Możesz być ambiwalentny, jeśli chodzi o wybór jednego modelu. Jeśli możesz to wszystko zrobić w jeden sposób, wybierz statyczny lub niestatyczny i idź z tym.


2

Mówiąc dokładniej w podanym przykładzie, wydaje się, że celem zdefiniowania tych metod jest bardziej przejrzystość kodu podczas czytania niż funkcjonalność (są one zdefiniowane jako prywatne). W takim przypadku korzystanie ze statycznego naprawdę nic dla ciebie nie robi, ponieważ celem statycznego jest ujawnienie funkcjonalności klasy.


Ta niewielka odpowiedź, ukryta pod obciążeniem innymi odpowiedziami, naprawdę dotyka istoty sprawy: funkcjonalność na poziomie klasy vs. funkcjonalność na poziomie obiektu.
eljenso

Dzięki, el, naprawdę to doceniam. Nie miałbym nic przeciwko, by trochę zagłosować :)
nie

Dałem mu +1 w momencie pisania komentarza.
eljenso

1
Nie zgadzam się. Klarowność kodu jest nadal ważna dla metod prywatnych. Może to być mniej ważne, ale wciąż jest coś, o co należy dążyć.
LegendLength,

1

Jednym z powodów jest to, że wszystkie pozostałe są równe, statyczne wywołania metod powinny być szybsze. Metody statyczne nie mogą być wirtualne i nie należy domyślnie brać tego odniesienia.


Zobacz moją odpowiedź poniżej (dodałem analizę, gdy była już na -3, więc nie jest bardzo widoczna).
Michael Myers

1

Jak wiele osób powiedziało, uczyń to statycznym ! Oto zasada, którą stosuję: jeśli uważasz, że metoda jest tylko funkcją matematyczną tj. Jest bezstanowa, nie obejmuje żadnych zmiennych instancji (=> brak zmiennych niebieskiego koloru [w zaćmieniu] w metodzie), a wynik metoda będzie taka sama dla liczby „n” wywołań (przy tych samych parametrach, oczywiście), a następnie oznacz tę metodę jako STATIC.

A jeśli uważasz, że ta metoda będzie przydatna dla innej klasy, przenieś ją do klasy Util, w przeciwnym razie umieść metodę jako prywatną w tej samej klasie. (minimalizacja dostępności)


1

Off-Topic: Zachowałbym metody pomocnicze w samodzielnej klasie narzędziowej / pomocniczej z tylko metodami statycznymi.

Problem z posiadaniem metod pomocniczych w punkcie użycia (czytaj „tę samą klasę”) polega na tym, że ktoś z dalszej linii może po prostu opublikować własne niepowiązane metody pomocnicze w tym samym miejscu


-1: SO nie jest forum wiadomości. Poradzisz sobie, zadając właściwe pytanie.
Stu Thompson

1
Wolałbym mieć metody pomocnicze wraz z klasą, z którą są związane / powiązane jako metody statyczne. Co więcej, mogę ujawnić niektóre jako publiczne, jeśli powinny być używane zewnętrznie. W ten sposób łatwiej będzie utrzymać interfejs API dla tej klasy, jeśli ma być publicznie dostępny i intensywnie używany.
Ivaylo Slavov,

1
class Whatever {

    public static varType myVar = initializeClassVariable();

    private static varType initializeClassVariable() {

        //initialization code goes here
    }
}

Zaletą prywatnych metod statycznych jest to, że mogą być ponownie wykorzystane później, jeśli zajdzie potrzeba ponownego zainicjowania zmiennej klasy.


1
  1. Bez modyfikatora statycznego nie można dowiedzieć się, że metoda jest bezstanowa bez dodatkowej analizy, którą można łatwo wykonać, pisząc (ponownie) metodę.

  2. Wtedy „statyczny” modyfikator może dać ci pomysły na temat refaktoryzacji oprócz innych rzeczy, które inni mogą uznać za nieużyteczne. Np. Przeniesienie metody do jakiejś klasy Utility lub przekonwertowanie jej na metodę członka.


0

Zadeklarowałbym je jako statyczne, aby oznaczyć je jako bezpaństwowe.

Java nie ma lepszego mechanizmu dla drobnych operacji, które nie są eksportowane, więc myślę, że prywatna statyczna jest akceptowalna.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.