Если частные вспомогательные методы являются статическими, если они могут быть статическими
Предположим, у меня есть 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
как статических методов – или каких-либо особых причин?
Конечно, проще всего оставить их нестационарными, хотя они могут быть статическими, не вызывая никаких проблем.
- Как я могу получить значение переменной сеанса внутри статического метода?
- Является ли инициализация статической членской переменной C ++ streamобезопасной?
- Разница между статическим classом и одноэлементным шаблоном?
- Почему глобальные и статические переменные инициализируются значениями по умолчанию?
- преобразование двумерного массива в указатель на указатель
- Неразрешенный внешний символ для статических членов classа
- Существует ли static_warning?
- Должен ли быть объявлен «статический конечный регистратор» в ВЕРХНЕЙ ЧАСТИ?
- Разница между статическими методами и методами экземпляра
- Невозможно установить значение поля статического объекта (ошибка LNK2001: неразрешенный внешний символ)
- Объективные переменные уровня статического classа
- Как я могу получить содержимое ресурса из статического контекста?
- глобальная переменная в C статична или нет?
Я предпочитаю, чтобы такие вспомогательные методы были 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а.
-
Без статического модификатора вы не можете понять, что метод является безстоящим без дополнительного анализа, который можно легко сделать, когда вы (повторно) напишите метод.
-
Затем «статический» модификатор может дать вам идеи о рефакторинге, помимо других вещей, которые другие могут найти неиспользованными. Например, перемещение метода в некоторый class Utility или преобразование его в метод участника.
Я объявлял бы их статическими, чтобы помечать их как апатриды.
Java не имеет лучшего механизма для второстепенных операций, которые не экспортируются, поэтому я считаю, что приватная статика приемлема.
Как и многие люди, сделайте это как статичное ! Вот правило большого пальца, которое я следую: если вы считаете, что метод – это просто математическая функция, то есть он не имеет состояния, не включает никаких переменных экземпляра (=> нет синих цветовых варов [в затмении] в методе), а результат метод будет одинаковым для числа «n» вызовов (с теми же параметрами, конечно), затем отметьте этот метод как STATIC.
И если вы считаете, что этот метод будет полезен для другого classа, тогда переместите его в class Util, в противном случае добавьте метод как частный в sameclass. (минимизация доступности)