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