Должны ли частные вспомогательные методы быть статическими, если они могут быть статическими

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

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 как статические методы - или какая-то конкретная причина не делать этого?

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


person avalys    schedule 11.02.2009    source источник
comment
См. Также, когда не следует использовать ключевое слово static в Java: stackoverflow.com/questions/1766715/   -  person Mark Butler    schedule 12.02.2013


Ответы (21)


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

person Esko Luontola    schedule 11.02.2009
comment
Один из моих любимых потоков. Я использую NetBeans, и он показывает вызовы статических методов курсивным шрифтом. - person skiabox; 28.06.2012
comment
Обычно я объявляю эти вспомогательные методы как protected static, что делает их очень легко тестируемыми в тестовом классе того же пакета. - person James; 29.08.2014
comment
@James или просто static (если мы хотим просто для тестирования). - person mrod; 08.03.2016
comment
Не будет изменять состояние объекта - точное объяснение того, как отличить его от частного метода. - person HopeKing; 26.06.2017

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

Я бы сделал их статичными, так как обычно делаю это, если это вообще возможно. Но это только я.


РЕДАКТИРОВАТЬ: Этот ответ продолжает получать отрицательные голоса, возможно, из-за необоснованного утверждения о размере байт-кода. Так что я действительно проведу тест.

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

}

Для вызова статического метода требуются два байт-кода (byteops?): iconst_0 (для аргумента) и invokestatic.
Для вызова нестатического метода требуется три: aload_1 (для объекта TestBytecodeSize, я полагаю), iconst_0 (для аргумента) , и invokespecial. (Обратите внимание, что если бы это не были частные методы, это было бы invokevirtual вместо invokespecial; см. JLS §7.7 Вызов методов.)

Как я уже сказал, я не ожидаю большой разницы в производительности между этими двумя, кроме того факта, что invokestatic требуется на один байт-код меньше. invokestatic и invokespecial оба должны быть немного быстрее, чем invokevirtual, поскольку они оба используют статическое связывание вместо динамического, но я понятия не имею, быстрее ли один из них, чем другой. Я тоже не могу найти хороших ссылок. Ближайшее, что я могу найти, - это эта статья о JavaWorld 1997 года, который в основном повторяет то, что я только что сказал:

Самыми быстрыми инструкциями, скорее всего, будут invokespecial и invokestatic, потому что методы, вызываемые этими инструкциями, статически связаны. Когда JVM разрешает символическую ссылку для этих инструкций и заменяет ее прямой ссылкой, эта прямая ссылка, вероятно, будет включать указатель на фактические байт-коды.

Но с 1997 года многое изменилось.

Итак, в заключение ... Думаю, я все еще придерживаюсь того, что сказал раньше. Скорость не должна быть причиной выбора одного из них, поскольку это будет в лучшем случае микрооптимизация.

person Michael Myers    schedule 11.02.2009
comment
все эти отрицательные голоса за то, что кажется очень прямым ответом. +1 в интересах правды, справедливости, .... - person Steve B.; 12.02.2009
comment
Спасибо. (Хотя я мог бы удалить его и получить значок, пока он был на -3 .: D) - person Michael Myers; 12.02.2009
comment
может быть, ваше предположение, что это меньший байт-код, неверно? Я ставлю +1, чтобы настроить вселенную правильно, по крайней мере, пока кто-нибудь не покажет причину -3 .. :) - person Kip; 12.02.2009
comment
Компилятор JIT в любом случае будет встраивать и оптимизировать их, поэтому количество инструкций байт-кода мало влияет на то, насколько быстро он будет. - person Esko Luontola; 24.06.2009
comment
Вот почему я сказал, что не думаю, что это имеет какое-либо значение в скорости (а если бы это было так, вероятно, было бы слишком мало, чтобы иметь значение в целом). - person Michael Myers; 24.06.2009
comment
Прежде чем приступить к подобным микрооптимизациям, вы должны рассмотреть влияние компилятора горячей точки. Итог: напишите свой код для удобства чтения людьми и оставьте оптимизацию компилятору - person Ichthyo; 17.01.2014
comment
Я не думаю, что отрицательные голоса были вызваны обсуждением скорости, я думаю, что они были вызваны незнанием того, почему статические методы полезны. - person Jessica; 23.05.2019

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

person Steve B.    schedule 11.02.2009

Ответ ... это зависит от обстоятельств.

Если member - это переменная экземпляра, специфичная для объекта, с которым вы имеете дело, тогда зачем вообще передавать ее как параметр?

Например:

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 */ } 
}
person Powerlord    schedule 11.02.2009
comment
+1: Несмотря на то, что пример плохой, слишком много методов, которые МОГУТ быть статичными, - это плохой запах кода. Между прочим, статические методы на самом деле являются функциями, а не объектно-ориентированными методами. Они могут принадлежать как метод к другому классу, или вам может не хватать класса, к которому эта функция могла бы принадлежать как метод. - person Bill K; 28.09.2009

Одна из причин, по которой вы можете захотеть объявить статические вспомогательные методы, заключается в том, что вам нужно вызвать их в конструкторе класса «перед» 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 не может быть методом экземпляра в данном случае.

person oxbow_lakes    schedule 11.02.2009
comment
вы можете вызвать метод экземпляра в конструкторе, но если вы делегируете другому конструктору с помощью super (...) или this (...), вы не можете вызвать метод экземпляра до тех пор, пока не завершится делегирование (но статика хорошо, как вы указываете) - person Kip; 12.02.2009

Я не могу придумать явных преимуществ частного статического метода. При этом нет особых преимуществ в том, чтобы сделать их нестатичными. В основном это вопрос презентации: вы можете сделать их статичными, чтобы четко подчеркнуть тот факт, что они не изменяют объект.

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

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

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

person Axelle Ziegler    schedule 11.02.2009
comment
Преимущество отсутствия статичности в том, что кто-то может создать подкласс и переопределить поведение метода. - person Clint Miller; 12.02.2009
comment
Clint - это частный метод, поэтому вы не можете его переопределить. - person Bhushan Bhangale; 12.02.2009
comment
Точно, для частного метода я не думаю, что это имеет большое значение. Однако для общедоступных методов я согласен с тем, что вы говорите - person Axelle Ziegler; 12.02.2009

Правильный ответ:

любой метод, который не берет информацию из поля и не помещает информацию в поле, не обязательно должен быть методом экземпляра. Любой метод, который не использует и не изменяет какие-либо поля в своем классе или объекте, также может быть статическим методом.

person Gyan Singh    schedule 05.09.2011

или какая-либо конкретная причина не [объявлять их статическими]?

да.

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

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

Таким образом, вы создаете подкласс и возвращаете его в новых версиях, а поскольку методы были объявлены как методы экземпляра, вы просто позволяете полиморфизму выполнять свою работу.

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

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

См. Здесь видео по теме: Как создать хороший API и почему это важно

Хотя он не имеет прямого отношения к обсуждению метода «статический или экземплярный», он затрагивает некоторые интересные моменты в дизайне API.

person OscarRyz    schedule 12.02.2009
comment
Полностью согласен. Я обычно стараюсь вообще избегать статики и приватности именно по этой причине. Я думаю, что большинство других ответов упускают из виду суть. - person Clint Miller; 12.02.2009
comment
Мы говорим о частных помощниках, что означает, что они не являются частью общедоступного API класса. Это означает, что ничто не мешает вам позже предоставить другую реализацию, сделать ее нестатической или внести какие-либо другие изменения. - person Jonik; 12.02.2009
comment
Аааахмм ... это хороший аргумент, Джоник. Тем не менее, создание хорошей привычки должно быть достаточным основанием (субъективно, конечно). - person OscarRyz; 12.02.2009
comment
Полностью не согласен. Нет веских причин, по которым вы хотели бы изменить частный метод без изменения класса, в котором он определен. Единственный способ сделать то, что вы предлагаете, - это манипулировать байтовым кодом. - person Peter Lawrey; 13.02.2009
comment
То, что вы предлагаете, могло бы иметь смысл, если бы метод не был частным, но даже в этом случае я бы посоветовал вам проектировать на основе текущей потребности, а не на основе воображаемой потребности. - person Peter Lawrey; 13.02.2009

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

person Jon Onstott    schedule 25.04.2014
comment
Не стоит писать тесты для частных методов. См. здесь. Нет необходимости тестировать частный метод, статический он или нет. - person B W; 07.09.2017
comment
@BW Это очень субъективная вещь. Есть много веских причин для тестирования частного метода. - person ruohola; 17.09.2019

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

Это позволяет использовать его в других статических методах или при инициализации класса, то есть:

public class Example {
   //...

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

   //...
}
person Kip    schedule 11.02.2009

Статический / нестатический вопрос сводится к тому, «действительно ли мне нужно использовать объект этого класса»?

Итак, вы передаете объект между разными методами? Содержит ли объект информацию, полезную вне контекста статических методов? Есть ли причина не определять методы обоими способами, если вы будете использовать их обоими способами?

Если вы находитесь в этой дилемме, мне кажется, что у вас есть все данные, необходимые для метода, плавающие в вашем коде за пределами объекта. Это то, что вы хотите? Было бы проще всегда собирать эти данные в объект каждый раз? У вас может быть двойственное отношение к единой модели. Если вы можете сделать все одним способом, выберите статический или нестатический и следуйте ему.

person notnot    schedule 11.02.2009

В подобных случаях я предпочитаю создавать статические методы computeOne и computeMore. Причина: инкапсуляция. Чем меньше кода имеет доступ к реализации вашего класса, тем лучше.

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

person maxaposteriori    schedule 11.02.2009

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

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

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

person Bhushan Bhangale    schedule 12.02.2009
comment
но есть места, где можно вызвать только статический метод, как в моем примере и в примере Криса Маршалла - person Kip; 12.02.2009
comment
Да, Кип, ваш и Крис ответы верны. Я не комментировал их, поскольку это правильные ответы. Я говорил только о неправильных ответах. - person Bhushan Bhangale; 12.02.2009

Исходя из своего опыта, я бы сказал, что такие частные методы имеют тенденцию быть довольно универсальными и многоразовыми.

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

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

person Piotr Sobczyk    schedule 07.11.2012

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

person notnot    schedule 11.02.2009
comment
Этот небольшой ответ, похороненный под множеством других ответов, действительно затрагивает суть вопроса: функциональность уровня класса или функциональность уровня объекта. - person eljenso; 12.02.2009
comment
Спасибо, Эл, я очень ценю это. Я бы тоже не возражал против небольшого голосования :) - person notnot; 12.02.2009
comment
Я поставил ему +1 во время написания комментария. - person eljenso; 23.02.2009
comment
Я не согласен. Ясность кода по-прежнему важна для частных методов. Это может быть менее важно, но все же к этому нужно стремиться. - person LegendLength; 19.03.2009

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

person dsimcha    schedule 23.02.2009
comment
Смотрите мой ответ ниже (я добавил анализ после того, как он был уже на -3, поэтому он не очень заметен). - person Michael Myers; 23.02.2009

Как говорили многие, сделайте это как static! Вот правило большого пальца, которому я следую: если вы думаете, что метод - это просто математическая функция, то есть он не имеет состояния, не включает никаких переменных экземпляра (=> нет переменных синего цвета [в затмении] в method), и результат метода будет одинаковым для n вызовов (конечно, с теми же параметрами), затем пометьте этот метод как STATIC.

И если вы думаете, что этот метод будет полезен для другого класса, переместите его в класс Util, в противном случае поместите метод как закрытый в том же классе. (минимизация доступности)

person jai    schedule 25.11.2009

Вне темы: я бы сохранил вспомогательные методы в отдельном служебном / вспомогательном классе, содержащем только статические методы.

Проблема с наличием вспомогательных методов в момент использования (читай «тот же класс») заключается в том, что кто-то в дальнейшем может просто разместить свои собственные несвязанные вспомогательные методы в том же месте.

person Everyone    schedule 25.11.2009
comment
-1: SO - это не форум для обмена сообщениями. Вы добьетесь большего успеха, если зададите правильный вопрос. - person Stu Thompson; 06.01.2010
comment
Я бы предпочел иметь вспомогательные методы вместе с классом, к которому они привязаны / связаны как статические. Более того, я могу сделать некоторые из них общедоступными, если их следует использовать извне. Таким образом будет легче поддерживать API для этого класса, если предполагается, что он будет публично представлен и интенсивно использоваться. - person Ivaylo Slavov; 01.11.2011

class Whatever {

    public static varType myVar = initializeClassVariable();

    private static varType initializeClassVariable() {

        //initialization code goes here
    }
}

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

person mug896    schedule 05.08.2011

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

  2. Тогда модификатор static может дать вам идеи о рефакторинге, помимо прочего, что другие могут счесть бесполезным. Например. перемещение метода в какой-либо служебный класс или преобразование его в метод-член ..

person bodrin    schedule 25.10.2012

Я бы объявил их статичными, чтобы пометить их как апатриды.

В Java нет лучшего механизма для второстепенных операций, которые не экспортируются, поэтому я думаю, что частные статические данные приемлемы.

person Uri    schedule 11.02.2009