Должны ли частные вспомогательные методы быть статическими, если они могут быть статическими - PullRequest
192 голосов
/ 12 февраля 2009

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

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 ]

165 голосов
/ 12 февраля 2009

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

106 голосов
/ 12 февраля 2009

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

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

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

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

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

18 голосов
/ 12 февраля 2009

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

Если 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 */ } 
}
18 голосов
/ 12 февраля 2009

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

11 голосов
/ 12 февраля 2009

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

10 голосов
/ 12 февраля 2009

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

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

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

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

7 голосов
/ 05 сентября 2011

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

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

5 голосов
/ 25 апреля 2014

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

5 голосов
/ 12 февраля 2009

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

Да.

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

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

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

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

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

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

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

4 голосов
/ 12 февраля 2009

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

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

public class Example {
   //...

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

   //...
}
...