I used to chant the line about how static methods in Java were evil. In fact, you can find funny articles and stack Overflow discussions about how this is the case. So why am I shouting from the rooftops that statics are no longer evil?
To answer that question, it’s important to realize that everything in a given language is a tool. A means to an end. The reason features take on such negative vibes is when they are used for evil. Like sharing state between objects.
When a class has a static variable, it means that ALL instances have access to this one “thing”. This mechanism was used to bootstrap things like the Singleton Pattern and the Flyweight Pattern, both things that involved a shared state between all instances that came with nasty side effects and an aversions to testing.
Singleton instances showed up in public facing APIs (even some Java standard APIs) and hence became very hard to evolve as well as test. With 20/20 hindsight, it’s crystal clear how the world moved away from these mechanisms and toward Dependency Injection. These static instances were VERY resistant to extension, testing, and ensuring a consistent state, given they had a completely different lifecycle inside the JVM compared to “newed” objects.
Hence, static were dubbed EVIL. And this led to a Butlerian Jihad against all statics.
But the landscape has changed. That previous stuff was in the era of Java 1.2 through 1.4, a time we have almost put out of our memories thanks to the leap forward of Java 5. Today we stand on the mountain of Java 8, surrounded by streams, method references, functional interfaces, and IDEs ready to transform any SAM into a lambda.
The ability to use a side-effect free function like Math.max() inside a lambda by method reference illustrates a purity we haven’t seen since Java’s inception. The ability to write small, tight functions, test them extensively, and then inject them with no risk of side effects from the surrounding object is a feature not a bug.
Let me repeat: static methods with no side effects from the surrounding objects are quite useful.
Another feature now available are static imports, making it simpler to compose functions and use them in functional flows. Being able to chain functions together without collecting in between state has let us move away from imperative programming and toward functional, reactive programming.
Static objects holding persistent state are a huge risk. They persist state between unit tests unless deliberate steps are taken to setup things up properly. In a day and age where we are trying to run either 10 or 10,000 copies of our app simultaneously, static state is NOT Cloud Native. But side-effect free static functions are.
None of this was possible with Java 1.4. That’s why statics had very little upside back in the day. So the judgment it received was most justified. If you saw “static” in someone’s code, they were probably doing something VERY WRONG. (Albeit at the direction of some of the most popular books back then). I cast that keyword into the pit myself. But today is different and to repeat the same arguments in light of Java 8 is short sighted and pedantic.
Statics are a important and we should embrace them instead of burying them under a rock.
So go forth and use statics. But do it correctly. Think: small, composable, testable functions.
If all this talk about functional flows, side-effect free programming, and method references is whetting your appetite, be sure to go grab your copy of Learning Spring Boot 2.0 2nd Edition. The entire book is written in the functional paradigm thanks to Project Reactor, and built on top of the Spring portfolio’s state of the art code base.