Java 8 Functional Programming: Lazy Instantiation
Singletons often instantiate themselves lazily, and sometimes, if the object is heavy enough, class fields can be instantiated lazily.
Generally, when going the lazy route, the getter method (or accessor) has to have a block of code that checks whether the object is instantiated yet (and, if it isn’t, it needs to be instantiated) before returning it. This check is pointless once the object has been instantiated; it only serves to slow down a method that’s already (usually) blocking with synchronized or a Lock. Let’s look at a way to remove that code, shall we?
Disclaimer
I didn’t come up with this. I got it from Functional Programming in Java by Venkat Subramaniam. I highly recommend both the book and the author. Everything I’ve read by Venkat was well-done and easy to learn from.
How Does It Work?
The basic idea is to:
- replace the lazily instantiated field with a Supplier of the type you need.
- the Supplier instantiates the object (but doesn’t return it yet)
- then it sets the field to a new Supplier that only returns the instantiated object
- return the instance
So let’s see this in action. We’re going to have a class called Holder that wants to lazily instantiate an object of type Heavy. This code is compiled directly from Venkat’s book:
public class Holder { private Supplier heavy = () -> createAndCacheHeavy(); public Heavy getHeavy() { return heavy.get(); } private synchronized Heavy createAndCacheHeavy() { class HeavyFactory implements Supplier { private final Heavy heavyInstance = new Heavy(); public Heavy get() { return heavyInstance; } } if(!HeavyFactory.class.isInstance(heavy)) { heavy = new HeavyFactory(); } return heavy.get(); } }
Now, this code works just fine, but I find the implementation of createAndCacheHeavy to be unnecessarily confusing. The first time I saw this code, it took me quite a while to figure out what it was doing.
So let’s make just a few modifications to it, shall we? We’ll make it so it visibly looks like it’s following the steps I laid out before.
private Heavy createAndCacheHeavy() { Heavy instance = new Heavy(); heavy = () -> instance; return instance; }
Isn’t that better? It’s a heck of a lot simpler and cleaner than before, in my opinion. And it still works! Well, there’s a small caveat: in order to make the code thread-safe, you need to synchronize the getInstance() method instead of createAndCacheHeavy method. That change will slow down the code a little bit compared to Venkat’s, since his code doesn’t use synchronization once the HeavyFactory is in place. But it’s still faster than the old method that required a synchronization AND a conditional check every time.
So, this is some helpful code, but do you want to be typing that code in every time that you want to lazily instantiate something? I didn’t think so. So let’s make a class that will be reusable and make our lives a lot easier.
But first, just to show you how much easier it becomes to use, let me show you it being used.
Supplier<Heavy> heavy = LazilyInstantiate.using(() -> new Heavy());
That’s it! Let’s look at this a little closer, and dig into it to see what’s going on before we make it.
The declaration bit of the line is the same as it was before; a Supplier of Heavy named heavy. But then we call a static method of LazilyInstantiate which turns out to be a static factory method that returns a LazilyInstantiate object which implements Supplier. The argument passed into the method is a Heavy Supplier that is there so the user can supply the instantiator with the correct code to instantiate the object.
So, are you curious as to how it works? Well here’s the code for LazilyInstantiate:
public class LazilyInstantiate implements Supplier { public static LazilyInstantiate using(Supplier supplier) { return new LazilyInstantiate<>(supplier); } public synchronized T get() { return current.get(); } private LazilyInstantiate(Supplier supplier) { this.supplier = supplier; this.current = () -> swapper(); } private final Supplier supplier; private Supplier current; private T swapper() { T obj = supplier.get(); current = () -> obj; return obj; } }
You may find the order of my methods and such to be a bit different than what is usual. I prefer to have public stuff first, then package-private and protected, then private. Within those chunks, I do static fields, then constructors, then static methods, then normal fields, then normal methods. In general, this seems to sort things in the order of most important to user reading my code to least important.
You are free to copy this code anywhere you want, or you can check out my functional-java library on github, which has a fully documented version of this class (func.java.lazy.LazilyInstantiate) and many other helpful functional classes.
Reference: | Java 8 Functional Programming: Lazy Instantiation from our JCG partner Jacob Zimmerman at the Programming Ideas With Jake blog. |
If you use Venkat’s original solution in implementing LazilyInstantiate, then you hide its complexity and you get its better synchronization characteristics.