Какая выгода от объявления метода статическим


94

Недавно я просматривал свои предупреждения в Eclipse и наткнулся на это:

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

Он выдаст предупреждение компилятору, если метод может быть объявлен как статический.

[править] Точная цитата в справке Eclipse, с акцентом на частные и окончательные:

При включении компилятор выдает ошибку или предупреждение для методов, которые являются закрытыми или окончательными и относятся только к статическим членам.

Да, я знаю, что могу его выключить, но я хочу знать причину включения?

Почему было бы хорошо объявить все возможные методы статическими?

Даст ли это какие-то преимущества в производительности? (в мобильном домене)

Указывая на метод как на статический, я полагаю, это показывает, что вы не используете никакие переменные экземпляра, поэтому их можно переместить в класс стиля utils?

В конце концов, следует ли мне просто отключить это «игнорирование» или исправить более 100 предупреждений, которые он мне выдал?

Вы думаете, что это просто лишние ключевые слова, которые загрязняют код, поскольку компилятор все равно просто встроит эти методы? (вроде как вы не объявляете каждую переменную, которую вы можете final, но вы могли бы ).


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

1
Мне любопытно, какую функциональность выполняют эти методы. Может быть, что-то было сделано не очень правильно, если их так много.
ArjunShankar

Ответы:


132

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

Когда метод является статическим, вы не можете получить доступ к нестатическим членам; следовательно, ваши возможности уже. Итак, если вам не нужны и никогда не понадобятся (даже в подклассах) нестатические члены для выполнения вашего контракта, зачем предоставлять доступ к этим полям вашему методу? Объявление метода staticв этом случае позволит компилятору проверить, что вы не используете элементы, которые не собираетесь использовать.

Более того, это поможет людям, читающим ваш код, понять природу контракта.

Вот почему считается хорошим объявить метод, staticкогда он фактически реализует статический контракт.

В некоторых случаях ваш метод означает только что-то относительно экземпляра вашего класса, и бывает, что его реализация фактически не использует какое-либо нестатическое поле или экземпляр. В таких случаях вы не должны отмечать метод static.

Примеры того, где вы не могли бы использовать staticключевое слово:

  • Хук расширения, который ничего не делает (но может что-то делать с данными экземпляра в подклассе)
  • Очень простое поведение по умолчанию, которое можно настроить в подклассе.
  • Реализация обработчика событий: реализация будет зависеть от класса обработчика событий, но не будет использовать какое-либо свойство экземпляра обработчика событий.

1
+1 Речь идет о минимизации возможностей выстрелить себе в ногу и уменьшении того, что вам нужно знать, чтобы понять метод.
Питер Лоури

7
Кроме того, вам не нужно возиться с получением экземпляра только для вызова независимой, автономной функции.
Марко Топольник

1
Так что в других мирах любой метод, не использующий переменные экземпляра, должен быть объявлен как статический? Я всегда думал, что методы должны быть статическими, только если это желательно (например, методы универсальности).
Петр Менсик

18
@PetrMensik Если privateметод может быть объявлен статическим, то почти всегда так и должно быть. Для любого другого уровня доступа необходимо учитывать другие факторы, например динамическую отправку.
Марко Топольник

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

15

Здесь нет понятия оптимизации.

staticМетод , staticпотому что вы явно объявить , что метод не опирается на какой - либо , например ограничивающий класс только потому , что это не нужно. Итак, предупреждение Eclipse, как указано в документации:

При включении компилятор выдает ошибку или предупреждение для методов, которые являются закрытыми или окончательными и относятся только к статическим членам.

Если вам не нужна никакая переменная экземпляра и ваш метод является частным (не может быть вызван извне) или окончательным (не может быть переопределен), тогда нет причин, чтобы он был обычным методом, а не статическим. Статический метод по своей сути более безопасен даже потому, что вам разрешено делать с ним меньше действий (ему не нужен экземпляр, у вас нет неявного thisобъекта).


7

У меня нет информации о производительности, я полагаю, что это немного лучше, самое большее, поскольку код не должен выполнять динамическую отправку в зависимости от типа.

Однако гораздо более веским аргументом против рефакторинга статических методов является то, что в настоящее время использование static считается плохой практикой. Статические методы / переменные плохо интегрируются в объектно-ориентированный язык, а также их трудно правильно протестировать. Это причина, по которой некоторые новые языки вообще отказываются от концепции статических методов / переменных или пытаются интернализовать их в язык таким образом, который лучше подходит для объектно-ориентированного программирования (например, объекты в Scala).

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


7
Статические методы легко тестировать (пока они самодостаточны - особенно чистые функции, которые являются основной целью статических методов). В этом случае объектно-ориентированной концепции нечего предложить. Кроме того, концепция функции первого класса имеет очень мало общего с концепцией статического метода. Если вам когда-нибудь понадобится первоклассная функция, выполняющая работу существующего статического метода, для ее реализации достаточно нескольких символов.
Марко Топольник

5
Замечание о тестируемости, вероятно, не относилось напрямую к статическому методу, но статические методы гораздо сложнее имитировать, поэтому они усложняют тестирование методов, вызывающих статические методы.
Buhb

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?

Наконец, я использовал Caliper, и результаты такие же, как при выполнении моих тестов вручную:

Для статических / динамических вызовов нет ощутимой разницы. По крайней мере, не для Linux / AMD64 / Java7.

Результаты Caliper здесь: https://microbenchmarks.appspot.com/runs/1426eac9-36ca-48f0-980f-0106af064e8f#r:scenario.benchmarkSpec.methodName,scenario.vmSpec.options.CMSLargeCoalSurplusPercentm. CMSLargeSplitSurplusPercent, сценарий.vmSpec.options.CMSSmallCoalSurplusPercent, сценарий.vmSpec.options.CMSSmallSplitSurplusPercent, сценарий.vmSpec.options.FLSLargestBlockCoalesceProximitySpec.

и мои собственные результаты:

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

Класс Caliper Test был:

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,
Бланделл

Ага. Я подумал, что вам будет интересно :) Но поскольку вы создаете программное обеспечение для 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
Что это за язык? Этот пример компилируется? Здесь нет ничего статичного.
Петр Перак

Действительно, кажется, я забыл «статику» из метода InvertText. И это пример, основанный на C #
NeroS 08
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.