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

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

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

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

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

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

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


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

 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."":()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 "":()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 требует меньше байт-кода. invokestatic и invokespecial должны быть немного быстрее, чем invokevirtual , поскольку оба они используют статическое связывание вместо динамического, но я понятия не имею, является ли он быстрее, чем другой. Я тоже не могу найти хороших ссылок. Самое близкое, что я могу найти, это статья 1997 JavaWorld , в которой в основном говорится, что я только что сказал:

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

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

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

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

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

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

Например:

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

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

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

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

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

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

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

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

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

Да.

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

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

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

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

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

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

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

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

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

 public class Example { //... //Only possible if computeOne is static public final static double COMPUTED_ONE = computeOne(new Something("1")); //... } 

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

 class Whatever { public static varType myVar = initializeClassVariable(); private static varType initializeClassVariable() { //initialization code goes here } } 

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

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

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

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

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

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

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

  • Какая польза от частной статической переменной в Java?
  • Что делает статическую переменную инициализацией только один раз?
  • Как я имитирую статические методы в classе с помощью easymock?
  • Java: статический и внутренний class
  • Унаследованы статические переменные
  • Инициализация статического члена в шаблоне classа
  • Какова цель статического ключевого слова в параметре массива функции типа «char s »?
  • Как создать экземпляр нестационарного внутреннего classа в статическом методе
  • Должны ли статические статические методы C #, которые * могут быть статическими?
  • Почему у нас нет статического метода в (нестационарном) внутреннем classе?
  • Разница между статическими и частными статическими переменными
  • Давайте будем гением компьютера.