Ishant Gaurav
Posted on June 13, 2021
We don’t have any Lazy<T>
initialisation functionality in Java as we have in other languages like Scala, C# etc. But don’t worry in Java we can implement the same kind of functionality by leveraging the Lambda’s and Functional interface.
In this example to create the lazy evaluation we will use the Supplier<T>
interface, although same can be implemented by applying the other interface as well.
Let’s understand first, How Supplier<T>
interface can be used to implement lazy initialisation.
Supplier<T>
interface represents a function which does not take in any argument but produces a value of type T. Suppliers are useful when we don’t need to supply any value and obtain a result at the same time.
Definition of Supplier<T>
interface is as below :
@FunctionalInterface
public interface Supplier<T> {
/**
* Gets a result.
*
* @return a result
*/
T get();
}
Lambda code of Supplier<T>
interface is only executed when we execute the get() method of Supplier interface.
If you create an object directly using the new operator, it eagerly creates the object where as if the same object is being created using the Supplier then it will be lazy initialisation as object will be created only when we call get() method of Supplier interface.
String name = "Ishant"; // eager
Supplier<String> v2 = () -> "Ishant"; // lazy
Let’s see the complete working example to understand it better.
package com.ishant.java8;
import java.util.function.Supplier;
class CustomObject {
CustomObject (String value) {
System.out.println(value);
}
public static CustomObject getObject(String value) {
return new CustomObject(value);
}
}
public class SupplierExample {
public static void main(String[] args) {
Supplier<CustomObject> customObjectSupplier = () -> CustomObject.getObject("Lazy initialization");
CustomObject customObject = CustomObject.getObject("Eager initialisation");
System.out.println("Supplier not called yet");
customObjectSupplier.get();
}
}
Output :
Supplier not called yet
Lazy initialization
In the above output, we can see that Lazy initialisation was printer after the Supplier<T>
get() method was executed.
Memoization Issue
Problem : Issue with the above approach is that, every time Supplier get() method is called new instance of CustomObject will be created.
We can verify the same with below example.
package com.ishant.java8;
import java.util.function.Supplier;
class CustomObject {
private static int value = 0;
CustomObject () {
value++;
System.out.println("Value : " + value);
}
public static CustomObject getObject() {
return new CustomObject();
}
}
public class SupplierExample {
public static void main(String[] args) {
Supplier<CustomObject> customObjectSupplier = () -> CustomObject.getObject();
customObjectSupplier.get();
customObjectSupplier.get();
customObjectSupplier.get();
}
}
Output :
Value : 1
Value : 2
Value : 3
As in the output above we can see that the value have been printed thrice, which means that on every get() call a new instance of CustomObject is being created.
How to fix this the memoization issue ?
Due to the not memoizing the instance of class issue, we won’t be able to use this Lazy initialisation technique where we need to maintain the singleton property of a class. For that what we can do is we can create another Lazy class and that class hold both a Supplier<T>
and the value T itself:
public class Lazy<T> implements Supplier<T> {
private final Supplier<T> supplier;
private T value;
@Override
public T get() {
if (value == null) {
value = supplier.get();
}
return value;
}
}
Let’s verify whether the above technique is maintaining the singleton property of a class or not as below :
package com.ishant.java8;
import java.util.function.Supplier;
class CustomObject {
private static int value = 0;
CustomObject () {
value++;
System.out.println("Value : " + value);
}
public static CustomObject getObject() {
return new CustomObject();
}
}
class Lazy<T> implements Supplier<T> {
private final Supplier<T> supplier;
private T value;
Lazy(Supplier<T> supplier) {
this.supplier = supplier;
}
@Override
public T get() {
if (value == null) {
value = supplier.get();
}
return value;
}
}
public class SupplierExample {
public static void main(String[] args) {
Lazy<CustomObject> customObjectSupplier = new Lazy(() -> CustomObject.getObject());
System.out.println("Call 1 : " + customObjectSupplier.get());
System.out.println("Call 2 : " + customObjectSupplier.get());
System.out.println("Call 3 : " + customObjectSupplier.get());
}
}
Output :
Value : 1
Call 1 : com.ishant.java8.CustomObject@566776ad
Call 2 : com.ishant.java8.CustomObject@566776ad
Call 3 : com.ishant.java8.CustomObject@566776ad
From the output, we can simply see that Value: 1 has been printed only once and on every call the hascode of the object is same.
Multithreading Issue :
We made a new class to lazily initialised the class and also maintained to create the only single instance of a class.
Problem : But in the above code singleton property of class won’t be applicable in multithreading environment as two threads might come to create the instance of class at the same time and both will be bound to create new instance, in that way multiple instance of class will be created .
Let’s verify the same with the below code :
package com.ishant.java8;
import java.util.function.Supplier;
class CustomObject {
private static int value = 0;
CustomObject () {
value++;
System.out.println("Value : " + value);
}
public static CustomObject getObject() {
return new CustomObject();
}
}
class Lazy<T> implements Supplier<T> {
private final Supplier<T> supplier;
private T value;
Lazy(Supplier<T> supplier) {
this.supplier = supplier;
}
@Override
public T get() {
if (value == null) {
value = supplier.get();
}
return value;
}
}
public class SupplierExample {
public static void main(String[] args) {
Lazy<CustomObject> customObjectSupplier = new Lazy(() -> CustomObject.getObject());
Runnable runnable = () -> customObjectSupplier.get();
Thread thread1 = new Thread(runnable);
Thread thread2 = new Thread(runnable);
Thread thread3 = new Thread(runnable);
Thread thread4 = new Thread(runnable);
thread1.start();
thread2.start();
thread3.start();
thread4.start();
}
}
Output :
Value : 3
Value : 3
Value : 4
Value : 3
As per the code output above, we can verify that value:3 and value:4 has been printed which means that multiple instance of same object has been created.
How to fix the multithreading issue ?
To fix the multithreading issue, we can simply make the get() method synchronised so that multiple thread can’t access the same get() at the same time. Also we can make the value property as volatile so that its value is write and read form the main memory only and not from the CPU cache.
Note : We can also implement the double check locking using synchronised block to make the code more proficient but to keep the simplicity for this code, i am just making the whole method synchronised.
Let’s see the code for above :
class Lazy<T> implements Supplier<T> {
private final Supplier<T> supplier;
private volatile T value;
Lazy(Supplier<T> supplier) {
this.supplier = supplier;
}
@Override
public synchronized T get() {
if (value == null) {
value = supplier.get();
}
return value;
}
}
Let’s see the complete example and verify above code :
package com.ishant.java8;
import java.util.function.Supplier;
class CustomObject {
private static int value = 0;
CustomObject () {
value++;
System.out.println("Value : " + value);
}
public static CustomObject getObject() {
return new CustomObject();
}
}
class Lazy<T> implements Supplier<T> {
private final Supplier<T> supplier;
private volatile T value;
Lazy(Supplier<T> supplier) {
this.supplier = supplier;
}
@Override
public synchronized T get() {
if (value == null) {
value = supplier.get();
}
return value;
}
}
public class SupplierExample {
public static void main(String[] args) {
Lazy<CustomObject> customObjectSupplier = new Lazy(() -> CustomObject.getObject());
Runnable runnable = () -> customObjectSupplier.get();
Thread thread1 = new Thread(runnable);
Thread thread2 = new Thread(runnable);
Thread thread3 = new Thread(runnable);
Thread thread4 = new Thread(runnable);
thread1.start();
thread2.start();
thread3.start();
thread4.start();
}
}
Output :
Value : 1
Output of the above code signify that only one instance has been created.
Final Code for Lazy Implementation :
class Lazy<T> implements Supplier<T> {
private final Supplier<T> supplier;
private volatile T value;
Lazy(Supplier<T> supplier) {
this.supplier = supplier;
}
@Override
public synchronized T get() {
if (value == null) {
value = supplier.get();
}
return value;
}
}
This Post was first published on my Personal blog website. If you like this post relevant, you cam further read other Post from there : https://ishantgaurav.in/
This is all from this Post, if you find anything wrong or have any other thoughts on the same , please share in the comment box below :
Posted on June 13, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.