Should Java methods be static by default?

后端 未结 23 1077
既然无缘
既然无缘 2020-12-04 09:24

Say you\'re writing method foo() in class A. foo doesn\'t ever access any of A\'s state. You know nothing else about what foo does, or how it behaves. It cou

相关标签:
23条回答
  • 2020-12-04 09:35

    Most static methods are written because

    1. You break down a complex method into submethods, or
    2. You wish String (or Date, or...) had some functionality that it doesn't have

    The first is not bad per se, but it's often a sign that you're missing objects. Instead of working with default types such as String or List, try inventing your own classes and move the static methods to those classes.

    The second reason produces the always-popular StringUtil, DateUtil, FooUtil classes. These are problematic because you have no way to discover that they exist, so programmers often write duplicates of these utility methods. The solution, again, is to avoid using String and Date all the time. Start creating your own objects, perhaps by wrapping the original object. The static methods become non-static methods of the new object.

    0 讨论(0)
  • 2020-12-04 09:36

    To answer the question on the title, in general, Java methods should not be static by default. Java is an object-oriented language.

    However, what you talk about is a bit different. You talk specifically of helper methods.

    In the case of helper methods that just take values as parameters and return a value, without accessing state, they should be static. Private and static. Let me emphasize it:

    Helper methods that do not access state should be static.


    1. Major advantage: the code is more expressive.

    Making those methods static has at least a major advantage: you make it totally explicit in the code that the method does not need to know any instance state.

    The code speaks for itself. Things become more obvious for other people that will read your code, and even for you in some point in the future.

    2. Another advantage: the code can be simpler to reason about.

    If you make sure the method does not depend on external or global state, then it is a pure function, ie, a function in the mathematical sense: for the same input, you can be certain to obtain always the same output.

    3. Optimization advantages

    If the method is static and is a pure function, then in some cases it could be memoized to obtain some performance gains (in change of using more memory).

    4. Bytecode-level differences

    At the bytecode level, if you declare the helper method as an instance method or as a static method, you obtain two completely different things.

    To help make this section easier to understand, let's use an example:

    public class App {
        public static void main(String[] args) {
            WithoutStaticMethods without = new WithoutStaticMethods();
            without.setValue(1);
            without.calculate();
    
            WithStaticMethods with = new WithStaticMethods();
            with.setValue(1);
            with.calculate();
        }
    }
    
    class WithoutStaticMethods {
    
        private int value;
    
        private int helper(int a, int b) {
            return a * b + 1;
        }
    
        public int getValue() {
            return value;
        }
    
        public void setValue(int value) {
            this.value = value;
        }
    
        public int calculate() {
            return helper(value, 2 * value);
        }
    }
    
    class WithStaticMethods {
    
        private int value;
    
        private static int helper(int a, int b) {
            return a * b + 1;
        }
    
        public int getValue() {
            return value;
        }
    
        public void setValue(int value) {
            this.value = value;
        }
    
        public int calculate() {
            return helper(value, 2 * value);
        }
    }
    

    The lines we are interested in are the calls to helper(...) on the classes WithoutStaticMethods and WithStaticMethods.

    Without static methods

    In the first case, without static methods, when you call the helper method the JVM needs to push the reference to the instance to pass it to invokespecial. Take a look at the code of the calculate() method:

     0 aload_0
     1 aload_0
     2 getfield #2 <app/WithoutStaticMethods.value>
     5 iconst_2
     6 aload_0
     7 getfield #2 <app/WithoutStaticMethods.value>
    10 imul
    11 invokespecial #3 <app/WithoutStaticMethods.helper>
    14 ireturn
    

    The instruction at 0 (or 1), aload_0, will load the reference to the instance on the stack, and it will be consumed later by invokespecial. This instruction will put that value as the first parameter of the helper(...) function, and it is never used, as we can see here:

    0 iload_1
    1 iload_2
    2 imul
    3 iconst_1
    4 iadd
    5 ireturn
    

    See there's no iload_0? It has been loaded unnecessarily.

    With static methods

    Now, if you declare the helper method, static, then the calculate() method will look like:

     0 aload_0
     1 getfield #2 <app/WithStaticMethods.value>
     4 iconst_2
     5 aload_0
     6 getfield #2 <app/WithStaticMethods.value>
     9 imul
    10 invokestatic #3 <app/WithStaticMethods.helper>
    13 ireturn
    

    The differences are:

    • there's one less aload_0 instruction
    • the helper method is now called with invokestatic

    Well, the code of the helper function is also a little bit different: there's no this as the first parameter, so the parameters are actually at positions 0 and 1, as we can see here:

    0 iload_0
    1 iload_1
    2 imul
    3 iconst_1
    4 iadd
    5 ireturn
    

    Conclusion

    From the code design angle, it makes much more sense to declare the helper method static: the code speaks for itself, it contains more useful information. It states that it does not need instance state to work.

    At the bytecode level, it is much more clear what is happening, and there's no useless code (that, although I believe the JIT has no way to optimize it, would not incur a significant performance cost).

    0 讨论(0)
  • 2020-12-04 09:39

    Plenty of interesting answers.

    If you desperately seek a rule, then use this:

    If the code is only ever used by instance methods of a single class, then make it an instance method - it is simply an extraction of code out of an instance context - which could be refactored back into (or out of) methods that access instance state.

    If the code is used by MORE THAN ONE class, and contains no access to instance variables in the class in which the method resides, then make it static.

    End of story.

    0 讨论(0)
  • 2020-12-04 09:40

    When you write a static method you should keep in mind that you'r gonna use it at use-site with static-import (make it look class free) and thus it should behave just like a function which doesn't something and may or may not return something and is isolated with the state of class it belongs to. So static methods should be a rare situation.

    If you seem to be making a lot of helper methods, then consider using package-private instance methods instead of private ones. Less typing, less boilerplate since you can re-use them as a helper to other classes in the same package.

    0 讨论(0)
  • 2020-12-04 09:42

    I find it difficult to subscribe to those avoid-static-methods theories. They are there to promote a completely sanitary object-oriented model anti-septically cleansed of any deviation from object relationships. I don't see any way essential to be anti-septically pure in the practice object-orientedness.

    Anyway, all of java.util.Arrays class are static. Numeric classes Integer, Boolean, String have static methods. Lots of static methods. All the static methods in those classes either convert to or from their respective class instances.

    Since good old Gosling, et al, proved to be such useful role models of having static methods - there is no point avoiding them. I realise there are people who are perplexed enough to vote down my response. There are reasons and habits why many programmers love to convert as much of their members to static.

    I once worked in an establishment where the project leader wanted us to make methods static as much as possible and finalize them. On the other hand, I am not that extreme. Like relational database schema design, it all depends on your data modelling strategy.

    There should be a consistent reason why methods are made static. It does not hurt to follow the standard Java library pattern of when methods are made static.

    The utmost importance is programming productivity and quality. In an adaptive and agile development environment, it is not only adapting the granularity of the project to respond effectively to requirements variation, but also adapting programming atmosphere like providing a conformable coding model to make best use of the programming skill set you have. At the end of the day (a project almost never ends), you want team members to be efficient and effective, not whether they avoided static methods or not.

    Therefore, devise a programming model, whether you want MVP, injection, aspect-driven, level of static-avoidance/affinity, etc and know why you want them - not because some theoretical nut told you that your programming practice would violate oo principles. Remember, if you work in an industry it's always quality and profitability not theoretical purity.

    Finally what is object-oriented? Object-orientation and data normalization are strategies to create an orthogonal information perspective. For example, in earlier days, IBM manuals were written to be very orthogonal. That is, if a piece of info is written somewhere in a page within those thousands of manuals, they avoid repeating that info. That is bad because you would be reading learning how to perform a certain task and frequently encounter concepts mentioned in other manuals and you would have to be familiar with the "data model" of the manuals to hunt those connecting pieces of info thro the thousands of manuals.

    For the same reason, OS/2 failed to compete with Microsoft because IBM's concept of orthogonality was purely machine and data based and IBM was so proudly declaring their true object-orientedness vs Microsoft's false object-orientedness pandering to human perspective. They had forgotten we humans have our own respective varying orthogonal perspectives of information that do not conform to data and machine based orthogonality or even to each other.

    If you are familiar with the topology of trees, you would realise that you could pick any leaf node and make it the root. Or even any node, if you don't mind having a multi-trunk tree. Everyone thinks his/her node is the root when in fact any could be the root. If you think your perspective of object-orientation is the canon, think again. More important is to minimise the number of nodes that are accepted as candidate roots.

    There needs to be a compromise between effectiveness and efficiency. There is no point in having an efficient data or object model that can be hardly effectively used by fellow programmers.

    0 讨论(0)
  • 2020-12-04 09:43

    No. Never. Static methods should be an exception. OO is all about having Objects with behaviour which revolves around the object's state. Imho, ideally, there shouldn't be any (or very few) static methods, because everything unrelated to the object's state could (and to avoid leading the concept of an object ad absurdum, should) be placed in a plain old function at module level. Possible exception for factories because Complex.fromCartesian (to take a wikipedia example) reads so well.

    Of course this (edit: Module-level functions) isn't possible in a single-paradigm OO language (edit: like Java) - that's why I'm such a devoted advocate of multi-paradigm language design, by the way. But even in a language exclusively OO, most methods will revolve around the object's state and therefore be nonstatic. That is, unless your design has nothing to do with OO - but in this case, you're using the wrong language.

    0 讨论(0)
提交回复
热议问题