Lazy assignment in Java
Programmers are inherently lazy and similis simili gaudet also like when the programs are lazy. Have you ever heard lazy loading? Or lazy singleton? (I personally prefer the single malt version though.) If you are programming in Scala or Kotlin, which is also a JVM language you can even evaluate expressions in a lazy way.
If you are programming in Scala you can write
1 | lazy val z = "Hello" |
and the expression will only be evaluated when z
is accessed the first time. If you program in Kotlin you can write something like
1 | val z: String by lazy { "Hello" } |
and the expression will only be evaluated when z
is accessed the first time.
Java does not support that lazy evaluation per se, but being a powerful language it provides language elements that you can use to have the same result. While Scala and Kotlin give you the fish, Java teaches you to catch your own fish. (Let’s put a pin in this thought.)
What really happens in the background, when you code the above lines in Scala and/or Kotlin, is that the expression is not evaluated and the variable will not hold the result of the expression. Instead, the languages create some virtual “lambda” expressions, a ‘supplier’ that will later be used to calculate the value of the expression.
We can do that ourselves in Java. We can use a simple class, Lazy
that provides the functionality:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | public class Lazy implements Supplier { final private Supplier supplier; private boolean supplied = false ; private T value; private Lazy(Supplier supplier) { this .supplier = supplier; } public static Lazy let(Supplier supplier) { return new Lazy(supplier); } @Override public T get() { if (supplied) { return value; } supplied = true ; return value = supplier.get(); } } |
The class has the public static
method let()
that can be used to define a supplier and this supplier is invoked the first time the method get()
is invoked. With this class, you can write the above examples as
1 | var z = Lazy.let( () -> "Hello" ); |
By the way, it seems to be even simpler than the Kotlin version. You can use the class from the library:
1 2 3 | com.javax0 lazylet 1.0.0 |
and then you do not need to copy the code into your project. This is a micro library that contains only this class with an inner class that makes Lazy
usable in a multi-thread environment.
The use is simple as demonstrated in the unit tests:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 | private static class TestSupport { int count = 0 ; boolean callMe() { count++; return true ; } } ... final var ts = new TestSupport(); var z = Lazy.let(ts::callMe); if ( false && z.get()) { Assertions.fail(); } Assertions.assertEquals( 0 , ts.count); z.get(); Assertions.assertEquals( 1 , ts.count); z.get(); Assertions.assertEquals( 1 , ts.count); |
To get the multi-thread safe version you can use the code:
01 02 03 04 05 06 07 08 09 10 | final var ts = new TestSupport(); var z = Lazy.sync(ts::callMe); if ( false && z.get()) { Assertions.fail(); } Assertions.assertEquals( 0 , ts.count); z.get(); Assertions.assertEquals( 1 , ts.count); z.get(); Assertions.assertEquals( 1 , ts.count); |
and get a Lazy
supplier that can be used by multiple threads and it is still guaranteed that the supplier passed as argument is passed only once.
Giving you a fish or teaching you to fish
I said to put a pin in the note “While Scala and Kotlin give you the fish, Java teaches you to catch your own fish.” Here comes what I meant by that.
Many programmers write programs without understanding how the programs are executed. They program in Java and they write nice and working code, but they have no idea how the underlying technology works. They have no idea about the class loaders, garbage collections. Or they do, but they do not know anything about the machine code that the JIT compiler generates. Or they even do that but they have no idea about the processor caches, different memory types, hardware architecture. Or they know that but have no knowledge about microelectronics and lithography and how the layout of the integrated circuits are, how the electrons move inside the semiconductor, how quantum mechanics determines the non-deterministic inner working of the computer.
I do not say that you have to be a physicist and understand the intricate details of quantum mechanics to be a good programmer. I recommend, however, to understand a few layers below your everyday working tools. If you use Kotlin or Scala it is absolutely okay to use the lazy structures they provide. They give a programming abstraction one level higher than what Java provides in this specific case. But it is vital to know how the implementation probably looks like. If you know how to fish you can buy the packaged fish because then you can tell when the fish is good. If you do not know how to fish you will rely on the mercy of those who give you the fish.
Published on Java Code Geeks with permission by Peter Verhas, partner at our JCG program. See the original article here: Lazy assignment in Java Opinions expressed by Java Code Geeks contributors are their own. |