Чи повинні приватні помічникові методи статичні, якщо вони можуть бути статичними


205

Скажімо, у мене клас, призначений для екземпляра. У мене є кілька приватних «помічників» методів всередині класу, які не потребують доступу до когось із членів класу, і діють виключно на їх аргументах, повертаючи результат.

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

Чи є якась конкретна причина вказувати computeOneі computeMoreяк статичні методи - чи якась конкретна причина цього не робити?

Безумовно, найпростіше залишити їх нестатичними, хоча вони, безумовно, можуть стати статичними, не викликаючи жодних проблем.


3
Дивіться також, коли не використовувати ключове слово static на Java: stackoverflow.com/questions/1766715/…
Марк Батлер

Відповіді:


174

Я вважаю за краще такі допоміжні методи private static; що дасть зрозуміти читачеві, що вони не будуть змінювати стан об’єкта. Мій IDE також показуватиме дзвінки до статичних методів курсивом, тому я буду знати, що метод є статичним, не шукаючи підпису.


2
Один з моїх улюблених тем. Я використовую NetBeans, і він показує статичні виклики методу з курсивним шрифтом.
skiabox

2
Я зазвичай декларую такі допоміжні методи, protected staticякі дозволяють зробити їх легко перевіряти в тестовому класі в тому ж пакеті.
Джеймс

2
@James або просто static(якщо ми просто хочемо для тестування).
mrod

3
"Не змінюватиме стан об'єкта" -досконале пояснення того, як відрізнити його від приватного методу.
HopeKing

110

Це може призвести до трохи меншого байт-коду, оскільки статичні методи не отримають доступ до них this. Я не думаю, що це має велику різницю у швидкості (і якби це було, то, ймовірно, це буде занадто мало, щоб змінити загальну зміну).

Я зробив би їх статичними, оскільки, як правило, роблю це, якщо це можливо. Але це тільки я.


EDIT: Ця відповідь не змінюється, можливо, через необґрунтоване твердження про розмір байтового коду. Тож я фактично проведу тест.

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

Байт-код (отриманий із 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

}

Викликання статичного методу займає два байт-коди (проміжки часу): iconst_0(для аргументу) та invokestatic.
Викликання нестатичного методу займає три: aload_1(для TestBytecodeSizeоб'єкта, я думаю), iconst_0(для аргументу) та invokespecial. (Зауважте, що якби це не були приватні методи, це було б invokevirtualзамість них invokespecial; див. JLS §7.7 Методи виклику .)

Тепер, як я вже сказав, я не сподіваюся, що між цими двома різними показниками ефективності між цими двома, окрім того, що invokestaticпотрібно один менший байт-код , я не очікую . invokestaticі invokespecialобидва повинні бути трохи швидшими invokevirtual, оскільки вони обидва використовують статичну прив'язку замість динамічної, але я не маю уявлення, чи то інший швидший за інший. Я не можу знайти жодних хороших посилань. Найближче, що я можу знайти, - це стаття JavaWorld 1997 року , яка в основному повторює те, що я щойно сказав:

Швидкіші інструкції, швидше за все, будуть invokespecialі invokestaticтому, що методи, на які посилаються ці інструкції, статично пов'язані. Коли JVM вирішить символічне посилання на ці вказівки і замінить його прямим посиланням, це пряме посилання, ймовірно, буде включати вказівник на фактичні байт-коди.

Але багато речей змінилося з 1997 року.

Отже, на закінчення ... Я думаю, я все ще дотримуюся того, що я говорив раніше. Швидкість не повинна бути причиною вибору одного за іншим, оскільки це була б оптимізація в кращому випадку.


всі ці негативні голоси за те, що здається дуже прямолінійною відповіддю. +1 в інтересах правди, справедливості, ....
Стів Б.

Дякую. (Хоча я міг її видалити і отримав значок, коли він був у -3 .: D)
Майкл Майєрс

2
Компілятор JIT все одно вбудовує та оптимізує їх, тому кількість інструкцій щодо байт-коду має мало спільного з тим, наскільки це буде швидко.
Еско Луонтола,

3
Ось чому я сказав: "Я не думаю, що це має різницю в швидкості (і якщо б це було, то, ймовірно, це буде занадто мало, щоб змінити загальну зміну)".
Майкл Майерс

7
Перш ніж зайнятися будь-якими мікрооптимізаціями подібного типу, слід розглянути ефект компілятора гарячих точок. Підсумок: напишіть свій код для читабельності людьми та залиште оптимізацію компілятору
Ichthyo

18

Моє особисте перевагу було б оголосити їх статичними, оскільки це чіткий прапор, що вони без громадянства.


18

Відповідь ... це залежить.

Якщо член є змінною екземпляра, характерною для об'єкта, з яким ви маєте справу, то навіщо взагалі передавати його як параметр?

Наприклад:

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: Незважаючи на те, що приклад поганий, занадто багато методів, які БУДЬ статичні, є поганим запахом коду. До речі, статичні методи - це фактично функціони, вони зовсім не є ОО. Вони можуть належати як метод в іншому класі, або вам може бути відсутній клас, до якого ця функція могла б належати як метод.
Білл К

11

Однією з причин, можливо, ви захочете оголосити статичні методи помічників - це якщо вам потрібно викликати їх у конструкторі класу "до" thisабо " super. Наприклад:

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

Це трохи надуманий приклад, але явно recoverIntне може бути методом екземпляра в цьому випадку.


ви можете викликати метод екземпляра в конструкторі, але якщо ви делегуєте інший конструктор із супер (...) або цим (...), ви не можете викликати метод екземпляра до делегування (але статики є добре, як ви зазначаєте)
Кіп

10

Я не можу реально думати про явні переваги для приватного статичного методу. Незважаючи на те, конкретних переваг для того, щоб зробити їх нестатичними, немає. Це головним чином питання презентації: ви можете зробити їх статичними, щоб чітко підкреслити той факт, що вони не змінюють об'єкт.

Для методу з різними привілеями доступу, я думаю, є два основні аргументи:

  • статичні методи можна викликати без створення екземпляра об'єкта, який може бути корисним
  • статичні методи не можуть бути успадковані, що може бути проблемою, якщо вам потрібен поліморфізм (але це не має значення для приватних методів).

Крім того, різниця досить мала, і я сильно сумніваюся, що додатковий цей покажчик, переданий методу екземпляра, суттєво зміниться.


4
Перевага не стати статичним полягає в тому, що хтось може підкласифікувати та змінити поведінку методу.
Клінт Міллер

7
Клінтіть його приватним методом, тому ви не можете перекрити метод.
Bhushan Bhangale

Саме для приватного методу я не думаю, що він має велике значення. Однак для публічних методів я згоден з тим, що ви говорите
Аксель Зіглер,

8

Правильна відповідь:

будь-який метод, який не бере жодної інформації з поля і не вводить будь-яку інформацію в поле, не повинен бути методом екземпляра. Будь-який метод, який не використовує та не змінює поля у своєму класі чи об'єкті, також може стати статичним методом.


5

чи якась конкретна причина не [оголосити їх статичними]?

Так.

Зберігаючи їх як методи екземплярів, ви дозволяєте надавати іншу реалізацію пізніше.

Це може здатися нерозумним (і насправді це було б, якби ці методи використовуються тільки ви в програмі на 50 рядків), але у великих програмах або в бібліотеках, якими користується хтось інший, ви можете вирішити вибрати кращу реалізацію, але не хочу зламати існуючий код.

Таким чином, ви створюєте підклас і повертаєте це в нових версіях, і оскільки методи були оголошені як екземплярні методи, ви просто дозволите поліморфізму робити свою роботу.

Крім того, ви можете скористатися приватним конструктором та надати статичний заводський метод з тієї ж причини.

Отже, моя рекомендація - зберігати їх як методи екземпляра та уникати статичних, якщо можливо.
Скористайтеся динамізмом, який надає мова.

Дивіться тут дещо пов’язане відео: Як створити хороший API та чому це має значення

Хоча це не пов'язане безпосередньо з обговоренням методу "статичний проти екземпляра", воно торкається деяких цікавих моментів в розробці API.


1
Повністю згоден. Зазвичай я намагаюся уникати статики і взагалі приватних причин саме з цієї причини. Я думаю, що більшість інших відповідей пропустили суть.
Клінт Міллер

22
Ми говоримо про приватних помічників, а це означає, що вони не є частиною загальнодоступного API класу. Це означає, що ніщо не заважає вам надавати іншу реалізацію пізніше, роблячи їх нестатичними або вносити будь-які інші зміни.
Jonik

Аааамм ... це хороший момент Джоніка. І все-таки для створення доброї звички має бути достатньо причин (суб'єктивно кажучи звичайно)
OscarRyz

2
Повністю не згоден. Немає вагомих причин ви хочете змінити приватний метод без зміни класу, в якому він визначений. Єдиний спосіб зробити те, що ви пропонуєте, - це маніпуляція байтовим кодом.
Пітер Лорі

2
Те, що ви пропонуєте, може мати сенс, якби метод не був приватним, але навіть тоді я б запропонував вам розробити на основі необхідного струму, а не проектувати на основі уявної потреби.
Пітер Лорі

5

Одне питання щодо статичних методів полягає в тому, що це може ускладнити використання об'єкта в одиничних тестах . Mockito не може створювати макети для статичних методів, і ви не можете створити реалізацію методу підкласу.


2
Не слід писати тести для приватних методів. Дивіться тут . Немає потреби тестувати приватний метод, статичний він чи ні.
BW

@BW Це дуже суб'єктивна річ. Існує багато вагомих причин для перевірки приватного методу.
ruohola

4

Якщо метод є лише підпрограмою, яка ніколи не може використовувати інформацію про стан, оголосити його статичним.

Це дозволяє використовувати його в інших статичних методах або в ініціалізації класів, тобто:

public class Example {
   //...

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

   //...
}

3

Статичне / нестатичне питання зводиться до "чи мені справді потрібно використовувати об'єкт цього класу"?

Отже, чи передаєте ви об’єкт між різними методами? Чи об'єкт містить інформацію, корисну поза контекстом статичних методів? Чи є причина не визначати методи обома способами, якщо ви будете використовувати їх обома способами?

Якщо ви перебуваєте в цій дилемі, мені здається, що у вас є всі дані, необхідні для методу, що плаває навколо у вашому коді поза об'єктом. Це те, чого ти хочеш? Чи було б простіше просто збирати ці дані в об’єкт кожного разу? Ви можете просто неоднозначно ставитись до єдиної моделі. Якщо ви можете все це зробити одним способом, то виберіть статичний або нестатичний і перейдіть з ним.


3

Мої переваги у таких випадках - робити computeOneіcomputeMore статичні методи. Причина: інкапсуляція. Чим менше код має доступ до реалізації вашого класу, тим краще.

У наведеному прикладі ви заявляєте, що computeOneі computeMoreне потрібно мати доступ до внутрішніх даних класу, тому навіщо давати можливість керівникам класу втручатися у внутрішні.


3

Я хотів би уточнити декілька речей, які інші плакати сказали, що дають неправильну інформацію.

По-перше, оскільки методи приватні, навіть якщо ви оголосите їх статичними, ви не зможете отримати доступ до них поза цим класом. По-друге, вони є приватними, тому ви навіть не можете переосмислити підклас, тому статичне чи нестатичне значення не має ніякого значення. По-третє, нестатичний приватний метод також може бути викликаний від конструктора класу, він не повинен бути статичним.

Тепер перейдемо до вашого питання, чи слід визначити метод приватного помічника як статичний чи нестатичний. Я піду з відповіддю Стіва, оскільки маркування приватного методу статичним показує, що цей метод без громадянства, оскільки я також дотримуюся цього правила, коли кодую.


але є місця, де можна назвати лише статичний метод, як у моєму прикладі та на прикладі Кріса Маршалла
Kip

Так, виправдайте відповіді та відповіді Кріса. Я не коментував їх, оскільки це правильні відповіді. Я говорив лише про неправильні відповіді.
Bhushan Bhangale

3

З досвіду я зазначу, що такі приватні методи, як правило, є досить універсальними та багаторазовими.

Я думаю, що перше, що потрібно зробити, - це поставити питання, чи може метод бути корисним поза поточного контексту класу. Якщо так, я б зробив саме те, що всі пропонують, і вилучив би цей метод як статичний до якогось класу утиліт, де хтось, сподіваємось, перевіряє, перш ніж впроваджувати новий метод, роблячи абсолютно те саме.

Такі приватні методи загального користування є джерелом значної частини дублювання коду в проекті, оскільки кожен розробник самостійно переосмислює їх саме там, де їй потрібно його використовувати. Тож централізація таких методів - це шлях.


2

Більш конкретно на прикладі ви дали, здається , що мета визначення цих методів більше для коду ясності , коли ви читаєте це , ніж для функціональності (вони будуть визначені як приватні). У такому випадку перехід із статикою насправді нічого не робить для вас, оскільки мета статики - розкрити функціональність класу.


Ця невелика відповідь, похована під навантаженням інших відповідей, дійсно торкається суті питання: функціональність рівня класу проти функціональності рівня об'єкта.
eljenso

Дякую, ел, я дуже ціную це. Я б не проти трохи до голосування, або :)
notnot

Я дав це +1 під час написання коментаря.
eljenso

1
Я не погоджуюсь. Чіткість коду досі важлива для приватних методів. Це може бути менш важливим, але все-таки варто прагнути.
LegendLength

1

Однією з причин є те, що при рівності інших статичні виклики методу повинні бути швидшими. Статичні методи не можуть бути віртуальними, і це посилання не береться неявно.


Дивіться мою відповідь нижче (я додав аналіз після того, як він уже був у -3, тому він не дуже видно).
Майкл Майерс

1

Як багато хто казав, зробіть це як статичне ! Ось головне правило, яке я слідую: Якщо ви вважаєте, що метод є лише математичною функцією тобто він без стану, не залучає змінних екземплярів (=> у методі немає синього кольору, змінюється), а результат метод буде однаковим для 'n' кількості викликів (з тими ж параметрами, звичайно), потім позначте цей метод як STATIC.

І якщо ви думаєте, що цей метод буде корисним для іншого класу, тоді перенесіть його до класу Util інакше, поставте метод як приватний у той самий клас. (мінімізація доступності)


1

Off-Topic: Я б утримував допоміжні методи в окремому утиліті / класі помічника, лише в ньому є статичні методи.

Проблема з тим, що в точці використання помічницьких методів (читайте "той самий клас") полягає в тому, що хтось із лінії може просто вибрати розміщення власних непов'язаних допоміжних методів у тому самому місці


-1: ТАК не форум для обміну повідомленнями. Ви зробите краще, фактично задавши відповідне запитання.
Стю Томпсон

1
Я вважаю за краще використовувати допоміжні методи разом з класом, до якого вони пов'язані / пов'язані як статичні. Навіть більше, я можу викрити деяких як публічних, якщо вони повинні використовуватися зовні. Таким чином буде простіше підтримувати API для цього класу, якщо він повинен бути загальнодоступним та інтенсивно використовуватися.
Івайло Славов

1
class Whatever {

    public static varType myVar = initializeClassVariable();

    private static varType initializeClassVariable() {

        //initialization code goes here
    }
}

Перевага приватних статичних методів полягає в тому, що вони можуть бути використані пізніше, якщо вам потрібно повторно ініціалізувати змінну класу.


1
  1. Без статичного модифікатора ви не можете зрозуміти, що метод без стану без додаткового аналізу, який можна легко зробити, коли ви (повторно) пишете метод.

  2. Тоді "статичний" модифікатор може дати вам ідеї щодо рефакторингу, окрім інших речей, які інші можуть вважати непотрібними. Наприклад, переміщення методу до якогось класу Utility або перетворення його в метод-член.


0

Я б оголосив їх статичними, щоб позначити їх як без громадянства.

У Java немає кращого механізму для незначних операцій, які не експортуються, тому я вважаю, що приватна статика є прийнятною.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.