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

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

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 как статические методы - или не?

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

21 ответов


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


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

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


EDIT: этот ответ продолжает получать downvoted, возможно, из-за необоснованного утверждения о размер байткода. Так что я проведу тест.

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 (для


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


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

Если 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 */ } 
}

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


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

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

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

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


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

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


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

да.

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

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

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

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

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

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

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


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

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

public class Example {
   //...

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

   //...
}

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


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

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


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

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

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


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

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

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


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


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

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

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


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


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

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


class Whatever {

    public static varType myVar = initializeClassVariable();

    private static varType initializeClassVariable() {

        //initialization code goes here
    }
}

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


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

  2. тогда" статический " модификатор может дать вам идеи о рефакторинге помимо других вещей, которые другие могут найти непригодными. Е. Г. перемещения метода к некоторым служебным классом, или преобразовать его в метод-член..


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

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


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

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