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

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

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:

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

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:

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:

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:

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 evaluated 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.

6 thoughts on “Lazy assignment in Java

  1. Iorek

    I am a “fallen physicist” understanding all the layers you mentioned but still feeling under educated and after all a bad programmer.

    Like

    Reply
    1. Peter Verhas Post author

      lorek, I know you from a long time ago. I can assure you, that you are not a bad programmer. If you said that in the field of programming I am more of an expert I accept that. There are other knowledge areas where you excel more. For example quantum mechanics. I am absolutely sure that you know more about the characteristics of the distribution function of the electrons in a covalently binding hydrogen molecule than what I know.

      Knowledge and experience in a specific area is not an absolute value.

      I am happy that you still keep reading my blogs. By the way, this also shows that you are not a bad programmer. A bad programmer by accident may read some of my articles, but no bad programmer would keep reading them all, and I know you do. Appreciate.

      Like

      Reply
  2. evik

    I am not sure how Kotlin or Scala handles it but in your solution if supplier throws an exception then Lazy starts to return null values instead of trying again. I would suggest to set supplied to true only after getting the result to avoid this.

    Like

    Reply
    1. Peter Verhas Post author

      Well, this is a non-defined functionality that can be imagined in many different ways.

      1. The current implementation will just return null for all subsequent calls, which makes sense because the value was not set.
      2. Your proposed behavior would repeatedly try to call the supplier that will trigger the side effects so many times as many times the method get() is invoked.

      3. I can also imagine a solution that throws the exception if it happens at the first invocation of the method get() and then it is thrown again if get() is invoked again.

      Why your solution is the only real one and any other behavior a misnomer?

      Like

      Reply
      1. evik

        I suggested a change and did not imply that it is the only way to go, for me it seemed more flexible.
        In case 1 the caller on subsequent calls has no means to tell the difference between a problem and a valid calculation as null might be a valid value.
        In case 2 if there are side effects that should happen only once and are repeated because of the exception then it is a design problem.
        Case 3 does not allow a chance to recover from an intermittent error (like network problem).

        Both case 1 and 3 are closing on the error and does not have the flexibility to recover. If you go with the second variation you can still add some code around to get the more fixed solutions if that is needed.

        Btw both Kotlin and Scala implements lazy with retries on exceptions.

        Like

  3. Pingback: Lazy task in Java | Hiya Android - Android World

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.