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

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

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 как статических методов - или какой-либо конкретной причины не для?

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

Ответ 1

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

Ответ 2

Это может привести к чуть меньшему байт-коду, поскольку статические методы не получат доступ к 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);
    }
}

Bytecode (получен с помощью 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 года многое изменилось.

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

Ответ 3

Ответ: это зависит.

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

Например:

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 */ } 
}

Ответ 4

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

Ответ 5

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

Ответ 6

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

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

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

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

Ответ 7

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

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

Ответ 8

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

Да.

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

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

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

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

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

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

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

Ответ 9

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

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

public class Example {
   //...

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

   //...
}

Ответ 10

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

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

Ответ 11

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

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

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

Ответ 12

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

Ответ 13

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

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

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

Ответ 14

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

Ответ 15

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

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

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

Ответ 16

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

Ответ 17

Off-Topic: Я бы сохранил вспомогательные методы в автономном классе utility/helper с использованием только статических методов.

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

Ответ 18

class Whatever {

    public static varType myVar = initializeClassVariable();

    private static varType initializeClassVariable() {

        //initialization code goes here
    }
}

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

Ответ 19

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

  • Затем "статический" модификатор может дать вам идеи о рефакторинге, помимо других вещей, которые другие могут найти неиспользованными. Например. перенос метода на некоторый класс Utility или преобразование его в метод участника.

Ответ 20

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

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

Ответ 21

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

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