Sometimes, being lazy is good. But that's no excuse for being lazy in work! It is only a valid excuse in programming if you apply at the right time and right place. The original article is here.
Our Simple Example of Null-Coalescing
Let's take this simple null-coalescing example:
var object = object == null
? getDefaultObject()
: object;
A simple but elegant solution when you want to assign a default value to a variable if the object is null. Classic Java 101!
Then, you get fancy and create a helper method to adhere to the DRY principle:
// Sweet, sweet juicy usage of generics
<T> T getOrDefault(T object, T defaultValue) {
return object == null
? defaultValue
: object;
}
Also, FYI:
DRY principle stands for Don't Repeat Yourself (ironically, I just repeated that)
And then, riding high on your DRY-fueled motivation, you refactor your entire code base like a caffeinated code warrior. You feel invincible. You feel productive. You feel... oh so proud of yourself.
Hold your horses, cowboy!
When Reality Hits (Hard)
Before your brain drowns in dopamine, and you start updating your LinkedIn with "Refactoring Ninja" as a skill, let me burst your bubble with a question:
What do you think will happen in this code snippet?
var object = getOrDefault(object, expensiveComputation());
Plot twist: The expensiveComputation() will ALWAYS execute, regardless of whether our lovely object is null or not. Surprise! Java won't help you optimize the hell out of this, so you are on your own!
The consequences?
Could range from wasting precious CPU cycles (your laptop fan is already judging you) to the dreaded "oh no, I've accidentally launched the nuclear missiles twice" scenario. That's programmer speak for "your non-idempotent operations fired twice and caused chaos that should've only happened once, but here we are, with two charges on the customer's credit card and an angry email in your inbox."
Catastrophic indeed.
Is There a Fix?
TL;DR: Yes, but you will need JDK 8+. Otherwise, you will need some ugly modification or even give up and switch to JavaScript or Python.
Use lazy evaluation.
Or in layman's terms, use Supplier<T>. Think of it as saying "I'll tell you the answer... but only when you actually need it."
Specifically, don't delete your current helper method yet (we're collectors, not destroyers). We'll add an overloaded one:
// Are you ready to get your OCP certificate?
// This is the bliss of Generics Usage
<T> T getOrDefault(T object, Supplier<? extends T> defaultValueSupplier) {
return object == null
? defaultValueSupplier.get()
: object;
}
And then, modify the expensive method call like this:
var object = getOrDefault(object, () -> expensiveComputation());
You can now rest easy knowing that expensiveComputation() will only be called if your shiny object is null. Otherwise? It stays asleep. Lazy. Unbothered. Living its best life.
And to think that the humble ternary operator from our first example could masterfully handle both eager and lazy evaluation without the hassle of having two methods. Truly a How do you do, fellow kids moment for the ages.
No, No, No, Don't Press the Delete Button!
But wait, don't delete the original helper method! It's still useful. Let's talk about when to use which:
The eager evaluation version is perfect for already computed values (simple getters, defined constants, that sort of thing). It's like having fast food: already prepared, ready to go, no waiting.
The lazy evaluation one is suitable when the default value requires invoking some rather expensive computations.
On the other hand: using lazy evaluation on already computed values is wasteful. You're creating a
Supplier<T>wrapper just to contain a value that's already sitting right there. It's like gift-wrapping a gift that's already unwrapped. Inefficient and slightly ridiculous.
Choose your approach carefully! Know what you need to do, and know which method to use (both versions are lovingly supported by Apache Commons Lang 3 libraries, bless their hearts). Our null-coalescing task is just a simple one, but the same principle can also be applied to other stuff, for example: hit the database only if the cache does not contain our desired value.
Top comments (0)