Should private helper methods be static if they ca

2019-01-03 04:09发布

Let's say I have a class designed to be instantiated. I have several private "helper" methods inside the class that do not require access to any of the class members, and operate solely on their arguments, returning a result.

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) {... } 
} 

Is there any particular reason to specify computeOne and computeMore as static methods - or any particular reason not to?

It is certainly easiest to leave them as non-static, even though they could certainly be static without causing any problems.

21条回答
三岁会撩人
2楼-- · 2019-01-03 04:28

One reason is that, all else being equal, static method calls should be faster. Static methods cannot be virtual, and do not take an implicit this reference.

查看更多
成全新的幸福
3楼-- · 2019-01-03 04:29

I would like to clarify few things which other posters have said as its giving wrong information.

Firstly since the methods are private even if you declare them static you will not be able to access them outside of this class. Secondly they are private so you can not even override in subclass so static or non-static doesn't make any difference. Thirdly a non-static private method can be called from a constructor of the class also, it need not be static.

Now coming to your question if a private helper method should be defined as static or non-static. I will go with Steve's answer as marking a private method static shows that this method is stateless as I also follow this rule when I code.

查看更多
走好不送
4楼-- · 2019-01-03 04:29
  1. Without the static modifier you cannot figure out that the method is stateless without additional analysis which can be easily done when you (re)write the method.

  2. Then "static" modifier may give you ideas about refactoring besides other things that others may find unuseful. E.g. moving the method to some Utility class or converting it to a member method..

查看更多
\"骚年 ilove
5楼-- · 2019-01-03 04:31

It might result in slightly smaller bytecode, since the static methods won't get access to this. I don't think it makes any difference in speed (and if it did, it would probably be too small to make a difference overall).

I would make them static, since I generally do so if at all possible. But that's just me.


EDIT: This answer keeps getting downvoted, possibly because of the unsubstantiated assertion about bytecode size. So I will actually run a test.

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 (retrieved with 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

}

Invoking the static method takes two bytecodes (byteops?): iconst_0 (for the argument) and invokestatic.
Invoking the non-static method takes three: aload_1 (for the TestBytecodeSize object, I suppose), iconst_0 (for the argument), and invokespecial. (Note that if these hadn't been private methods, it would have been invokevirtual instead of invokespecial; see JLS §7.7 Invoking Methods.)

Now, as I said, I don't expect there to be any great difference in performance between these two, other than the fact that invokestatic requires one fewer bytecode. invokestatic and invokespecial should both be slightly faster than invokevirtual, since they both use static binding instead of dynamic, but I have no idea if either is faster than the other. I can't find any good references either. The closest I can find is this 1997 JavaWorld article, which basically restates what I just said:

The fastest instructions will most likely be invokespecial and invokestatic, because methods invoked by these instructions are statically bound. When the JVM resolves the symbolic reference for these instructions and replaces it with a direct reference, that direct reference probably will include a pointer to the actual bytecodes.

But many things have changed since 1997.

So in conclusion... I guess I'm still sticking with what I said before. Speed shouldn't be the reason to choose one over the other, since it would be a micro-optimization at best.

查看更多
等我变得足够好
6楼-- · 2019-01-03 04:32

I would declare them as static to flag them as stateless.

Java does not have a better mechanism for minor operations that aren't exported, so I think private static is acceptable.

查看更多
forever°为你锁心
7楼-- · 2019-01-03 04:33

The answer is... it depends.

If member is an instance variable specific to the object you're dealing with, then why pass it as a parameter at all?

For instance:

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 */ } 
}
查看更多
登录 后发表回答