Який виграш від оголошення методу статичним


94

Нещодавно я переглядав свої попередження в Eclipse і стикався з цим:

статичне попередження

Це дасть попередження компілятору, якщо метод може бути оголошений статичним.

[редагувати] Точна цитата в довідці Eclipse, з акцентом на приватному та остаточному:

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

Так, я знаю, що можу його вимкнути, але я хочу знати причину його ввімкнення?

Чому було б добре оголосити кожен метод можливим як статичний?

Чи дасть це якісь переваги в роботі? (у мобільному домені)

Вказуючи метод на статичний, я вважаю, це показує, що ви не використовуєте жодних змінних екземпляра, отже, їх можна перенести в клас стилю utils?

Врешті-решт мені слід просто вимкнути це «ігнорувати» чи виправити 100+ попереджень, які він мені дав?

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


Не впевнений, але це просто можна розглядати як допоміжний засіб програмування. Попередження - це просто вказівки на те, на що слід дивитись.
James P.

1
Мені цікаво, яку функціональність виконують ці методи. Можливо, щось було зроблено не дуже правильно, якщо їх так багато.
ArjunShankar

Відповіді:


132

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

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

Більше того, це допоможе людям, які читають ваш код, зрозуміти природу договору.

Ось чому вважається хорошим оголосити метод, staticколи він насправді реалізує статичний контракт.

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

Приклади, де ви не використовували б staticключове слово:

  • Гачок розширення, який нічого не робить (але міг би щось робити з даними екземпляра в підкласі)
  • Дуже проста поведінка за замовчуванням, призначена для налаштування в підкласі.
  • Реалізація обробника подій: реалізація буде залежати від класу обробника подій, але не використовуватиме жодної властивості екземпляра обробника події.

1
+1 Це про зведення до мінімуму можливостей стріляти собі в ногу та зменшення того, скільки потрібно знати, щоб зрозуміти метод.
Пітер Лорі

7
Крім того, вам не потрібно возитися з придбанням екземпляра, щоб просто викликати незалежну, автономну функцію.
Marko Topolnik

1
Отже, в інших світах будь-який метод, який не використовує змінні екземпляра, повинен бути оголошений статичним? Я завжди вважав, що методи повинні бути статичними лише в тому випадку, якщо це бажано (наприклад, методи utitility).
Петр Менсік,

18
@PetrMensik Якщо privateметод можна оголосити статичним, то майже незмінно він повинен бути. Для будь-якого іншого рівня доступу слід враховувати інші фактори, наприклад, динамічне відправлення.
Marko Topolnik

1
@JamesPoulson Я маю на увазі динамічну диспетчеризацію методів, що відбувається під час object.method()вибору методу виклику.
Марко Топольник

15

Тут немає концепції з оптимізацією.

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

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

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


7

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

Однак набагато сильнішим аргументом проти рефакторингу на статичні методи є те, що в даний час використання статики вважається поганою практикою. Статичні методи / змінні погано інтегруються в об'єктно-орієнтовану мову, а також їх важко перевірити належним чином. Це є причиною того, що деякі новіші мови взагалі відмовляються від концепції статичних методів / змінних або намагаються інтегрувати її в мову таким чином, щоб вона краще відповідала ОО (наприклад, Об’єкти в Scala).

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


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

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

2
Статичні методи легко знущатись, якщо ви використовуєте потужний механізм насмішок , такий як JMockit , PowerMock або Groovy .
Джефф Олсон,

Це private staticметоди, тому вам не потрібно буде знущатися над ними
Бланделл

3

1. Метод декларуванняstaticдає невеликі переваги в продуктивності, але, що є більш корисним, дозволяє використовувати його, не маючи під рукою екземпляра об'єкта (подумайте, наприклад, про заводський метод або отримання сингтона). Це також служить документальній меті - розповісти про характер методу. Цю документальну мету не слід ігнорувати, оскільки вона дає негайну підказку про природу методу читачам коду та користувачам API, а також служить інструментом мислення для оригінального програміста - явна інформація щодо передбачуваного значення допомагає Ви також думаєте прямо і створюєте більш якісний код (я думаю, виходячи зі свого особистого досвіду, але люди різні). Наприклад, логічно і, отже, бажано розрізняти методи, що діють на тип, і методи, що діють на екземпляр типу (на що вказуєДжон Скіт у своєму коментарі до запитання на C # ).

Ще одним варіантом використання staticметодів є імітація інтерфейсу процедурного програмування. Подумайте про java.lang.System.println()клас та методи та атрибути в ньому. Клас java.lang.Systemвикористовується як простір імен групування, а не як екземплярний об'єкт.

2. Як Eclipse (або будь-який інший запрограмований чи інший вид - біокомпонується чи не біокомпонується - сутність) може точно знати, який метод можна оголосити статичним? Навіть якщо базовий клас не отримує доступ до змінних екземпляра або не викликає нестатичних методів, за механізмом успадкування все може змінитися. Тільки якщо метод неможливо замінити шляхом успадкування підкласу, ми можемо із 100% впевненістю стверджувати, що метод дійсно може бути оголошений static. Замінити метод неможливо саме в двох випадках існування

  1. private (жоден підклас не може використовувати його безпосередньо і навіть в принципі не знає про це), або
  2. final (навіть якщо доступний для підкласу, немає можливості змінити метод на посилання на дані екземпляра або функції).

Звідси логіка опції Eclipse.

3. Оригінальний плакат також запитує: " Вказуючи метод як статичний, я гадаю, це показує, що ви не використовуєте жодних змінних екземпляра, тому їх можна було б перемістити до класу стилю utils? " Це дуже хороший момент. Іноді на подібні зміни конструкції вказує попередження.

Це дуже корисна опція, яку я б особисто переконався увімкнути, якби я використовував Eclipse і якби я програмував на Java.


1

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

Ви також запитали про ефективність:

Можливо, буде незначний приріст продуктивності, оскільки для виклику статичного методу не потрібне неявне посилання "this" як параметр.

Однак цей вплив на продуктивність справді незначний. Отже, вся справа в обсязі.


1

З інструкцій щодо продуктивності Android:

Віддайте перевагу статичному перед віртуальним. Якщо вам не потрібно отримувати доступ до полів об’єкта, зробіть метод статичним. Запрошення будуть приблизно на 15% -20% швидшими. Це також хороша практика, оскільки з підпису методу ви можете зрозуміти, що виклик методу не може змінити стан об’єкта.

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


"те, що виклик методу не може змінити стан об'єкта" - неймовірно вводить в оману. Я не знаю про вас, але я, звичайно, вважаю статичні атрибути класу частиною стану об'єкта.
Адам Паркін,

0

Ну, документація Eclipse говорить про те, про яке питання йдеться:

Метод може бути статичним

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

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

Чесно кажучи, не думаю, що за цим стоїть якась таємнича причина.


0

Мені бракувало деяких цифр для різниці швидкості. Тому я спробував порівняти їх, що виявилося не так просто: цикл Java стає повільнішим після деяких прогонів / помилки JIT?

Нарешті я використав штангенциркуль, і результати такі самі, як запуск тестів вручну:

Немає вимірюваної різниці для статичних / динамічних дзвінків. Принаймні не для Linux / AMD64 / Java7.

Результати штангенциркуля знаходяться тут: https://microbenchmarks.appspot.com/runs/1426eac9-36ca-48f0-980f-0106af064e8f#r:scenario.benchmarkSpec.methodName,scenario.vmSpec.options.CMSLargeCoalSurpluspeopcent. CMSLargeSplitSurplusPercent, scenario.vmSpec.options.CMSSmallCoalSurplusPercent, scenario.vmSpec.options.CMSSmallSplitSurplusPercent, scenario.vmSpec.options.FLSLargestBlockCoalesceProximityConc.

і мої власні результати:

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

Клас тестування штангенциркуля був:

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

}

І моїм власним тестовим класом було:

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

}

Було б цікаво побачити результати на різних пристроях та версіях Android
Blundell

Так. Я думав, що вам буде цікаво :) Але оскільки ви створюєте програмне забезпечення для Android і, мабуть, маєте пристрій Android, підключений до вашої станції розробника, я пропоную вам просто вибрати код, запустити його та поділитися результатами?
Scheintod

-2

Методи, які ви можете оголосити статичними, - це методи, які не потребують створення екземплярів, наприклад

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

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

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

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

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


1
Що це за мова? Чи компілює цей приклад? Тут нічого не є статичним.
Piotr Perak

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