Banner

Tuesday, January 20, 2015

LazyProperty


Encapsulating Lazy Initialization With Suppliers


Now on GitHub
package com.nield.utilities;

import java.util.function.Supplier;

public final class LazyProperty<T> {
    private volatile T value;
    private final Supplier<T> supplier;
    
    private LazyProperty(Supplier<T> supplier) { 
        this.supplier = supplier;
    }
    public T get() { 
        if (value == null) { 
            synchronized(this) { 
                if (value == null) { 
                    value = supplier.get();
                }
            }
        }
        return value;
    }
    public void reset() {
        if (value != null) { 
            synchronized(this) { 
                if (value != null) { 
                    this.value = null;
                }
            }
        }
    }
    public static <B> LazyProperty<B> forSupplier(Supplier<B> supplier) { 
        return new LazyProperty<B>(supplier);
    }
}

Lazy initialization is deferring calculation of a value until it is needed, and then caching it for all uses thereafter. It often is used to hold off an expensive calculation and then saving the result for future uses. A typical lazy initialization would look something like this...
 
import java.math.BigDecimal;
import java.util.Date;

public final class FinanceDay {
 private final Date date = new Date();
 private BigDecimal balance;
 
 public Date getDate() { 
  return date;
 }
 public BigDecimal getBalance() { 
  if (balance == null) { 
      balance = BalanceCalculator.forDate(date);
  }
  return balance;
 }
}
The "balance" variable is initially null. When the getBalance() method is called, it first checks if it is null and calculates and assigns the "balance" value before returning it. After the initial call, it will not have to calculate it again as the "if (balance == null)" condition will return false.

Lazy initialization is not optimal for most cases. You should always strive to initialize values on construction. Even better, you should make them final and immutable if possible.

But there are certainly cases lazy initialization is applicable, especially where properties are either a) expensive to build or b) depend on the object already being constructed. I have found lazy initialization to be greatly needed in business decision process applications that go through a lot of decision calculations. This is not only expensive but also requires the business objects to be constructed and fully aware of themselves before they can engage with any algorithms. This is a good use of Lazy Initialization.

However, lazy initialization gets even more complicated when you multithread your application (which is inevitable for any reasonably complex application). To prevent race conditions or contention between threads, you have to synchronize on a lock and protect the "balance" value, and you have to check the null condition twice.
 
import java.math.BigDecimal;
import java.util.Date;

public final class FinanceDay {
 private final Date date = new Date();
 private BigDecimal balance;
 
 public Date getDate() { 
  return date;
 }
 public BigDecimal getBalance() { 
  if (balance == null) { 
         synchronized(this) {  
             if (balance == null) { 
                balance = BalanceCalculator.forDate(date);
             }
          }
        }
        return balance;
  }
}
If you have five properties that use lazy initialization in a multithreaded environment, your class getter methods can get messy very quickly like the one above. This pattern is very repetitive, and therefore is a good candidate to be encapsulated into a utility class. With a supplier lambda expression, it becomes even easier.


Here is LazyProperty, a solution to fulfill that need using all the means described.
 

import java.util.function.Supplier;

public final class LazyProperty<T> {
    private volatile T value;
    private final Supplier<T> supplier;
    
    private LazyProperty(Supplier<T> supplier) { 
        this.supplier = supplier;
    }
    public T get() { 
        if (value == null) { 
            synchronized(this) { 
                if (value == null) { 
                    value = supplier.get();
                }
            }
        }
        return value;
    }
    public static <B> LazyProperty<B> forSupplier(Supplier<B> supplier) { 
        return new LazyProperty<B>(supplier);
    }
}

LazyProperty takes care of this entire pattern and is generic so it works with any type. It also uses a lambda supplier to provide instructions on how to construct the value. Invoking LazyProperty is simple. Call LazyProperty.forSupplier() and pass a supplier lambda to the static method. It will return a new LazyProperty which will create and cache the value once called. It will also manage all thread synchronization so it is threadsafe. Use the get() method to extract the value out of LazyProperty.

 
import java.math.BigDecimal;
import java.util.Date;

public final class FinanceDay {
 private final Date date = new Date();
 private final LazyProperty<BigDecimal> balance = 
            LazyProperty.forSupplier(() -> BalanceCalculator.forDate(date));
 
 public Date getDate() { 
  return date;
 }
 public BigDecimal getBalance() { 
  return balance.get();
 }
}

Just make sure you do not create a stack overflow or null pointer exception by making the supplier call itself by calling the get() method of the LazyProperty it is populating. To my understanding, you can use references of  "this" in the supplier safely as the lambda is holding a soft reference to the class which may be in partially constructed state, but if implemented properly the get() will not be called until the entire class is constructed.

Optionally, you can implement a reset() method to clear the cached value so it can be recalculated again.

package com.nield.utilities;

import java.util.function.Supplier;

public final class LazyProperty<T> {
    private volatile T value;
    private final Supplier<T> supplier;
    
    private LazyProperty(Supplier<T> supplier) { 
        this.supplier = supplier;
    }
    public T get() { 
        if (value == null) { 
            synchronized(this) { 
                if (value == null) { 
                    value = supplier.get();
                }
            }
        }
        return value;
    }
    public void reset() {
        if (value != null) { 
            synchronized(this) { 
                if (value != null) { 
                    this.value = null;
                }
            }
        }
    }
    public static <B> LazyProperty<B> forSupplier(Supplier<B> supplier) { 
        return new LazyProperty<B>(supplier);
    }
}

The reset() method is also synchronized in similar fashion to the get() by only allowing one thread to check and change the value. Some die hard Immutable-ists like me could argue this introduces undesirable mutability and may present opportunity for bad mutable designs, but I cannot see the harm since the supplier is final. Assuming the supplier is designed correctly, the supplier should always return the same value every time, or the most up-to-date value. Abuse  could come from excessive calls to reset() which may hit performance. It could also encourage bad designs by allowing reuse of objects rather than creating new ones once properties change. But if used intelligently, it is nice to have reset() available for certain solutions.

Hope you find this utility helpful!

No comments:

Post a Comment