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


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

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

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

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

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

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

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

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

В конце дня я должен просто отключить "игнорировать" или я должен исправить 100+ предупреждения, он дал мне?

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

9 93

9 ответов:

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

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

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

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

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

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

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

здесь нет концепции с оптимизацией.

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

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

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

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

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

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

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.

см. ответ Сэмюэля о том, как изменяется область применения метода. Я думаю, это основной аспект создания статического метода.

вы также спросили о производительности:

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

однако, это влияние производительности действительно крошечное. Поэтому все дело в сфере применения.

из руководства по производительности Android:

предпочитайте статический над виртуальным, Если вам не нужно обращаться к объекту поля, сделайте свой метод статическим. Призывы будут около 15% -20% быстрее. Это также хорошая практика, потому что вы можете сказать из метода сигнатура, вызывающая метод, не может изменить состояние объекта.

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

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

метод может быть статическим

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

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

Я честно не думаю, что есть какая-то другая таинственная причина.

мне не хватало некоторых чисел для разницы в скорости. Поэтому я попытался сравнить их, что оказалось не так просто:цикл Java становится медленнее после некоторых запусков / ошибки JIT?

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

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

результаты суппорта здесь: https://microbenchmarks.appspot.com/runs/1426eac9-36ca-48f0-980f-0106af064e8f#r:scenario.benchmarkSpec.methodName,scenario.vmSpec.options.CMSLargeCoalSurplusPercent,scenario.vmSpec.options.CMSLargeSplitSurplusPercent,scenario.vmSpec.options.CMSSmallCoalSurplusPercent,scenario.vmSpec.options.CMSSmallSplitSurplusPercent,scenario.vmSpec.options.FLSLargestBlockCoalesceProximity,scenario.vmSpec.options.G1ConcMarkStepDurationMillis

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

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

}

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

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

... Но это то, что вы, вероятно, уже знаете. Это не дает вам никаких реальных преимуществ как таковых, кроме того, что более ясно, что метод не использует переменные экземпляра.

другими словами, вы можете наиболее безопасно просто повернуть его полностью выключен. Если вы знаете, что никогда не будете использовать метод в других классах (в этом случае он должен быть просто частным), вам не нужно, чтобы он был статическим вообще.