Laziness may not be a good thing in daily life, but could be a useful strategy in software development. Let's take a look at the following codes. Are there any differences between the two functions?
There is a huge difference between when someExpensiveOperation function is expressed in a statement and in an expression. In the second function, the program needs to evaluate the value returned by someExpensiveOperation function, before executing the orElse logic. In the first function, however, someExpensiveOperation function may not be executed if p is not null. Obviously the first one is better.
How can we get around this issue? We can use the provided orElseGet function as following:
OrElseGet takes a Supplier<T> functional interface, which will be invoked explicitly. Nowadays, lots of modern APIs provide this kind of laziness: deferring the evaluation till actually needed.
For example, if we would like to design a API that allows clients to make different strategies based on the person's age. We can write something like this:
Here we are providing the same API with two overloading. The second lazy API can grant clients more flexibility and potentially improve the performance (If they need to make network calls when generating different insurance strategies).
Laziness can also be helpful during object initialization. For example when you are designing a class representing a celebrity with bunch of awards:
You are making a database query to get the award list during object initialization, which may not be a good idea as the award list may never be accessed. You could make a simple change to solve the problem.
Please note there are some tradeoffs. You are improving the performance, but losing the ability (or simplicity) to make the above codes work correctly in the multi-thread environment. In addition, sometimes making a field lazy is not necessary. If by design the field will always be accessed, it is the waste of effort to do so.
Is it always good to be lazy? Probably not. Please see the following example that applies the quad tree algorithm to find the nearest points of interest within a certain range.
Before inserting into the priority queue, we test whether the point is within range, which is an eager approach. We can always dump all the points into the priority queue and check whether the top point falls in range, which is a lazy way. Doing eagerly will provide a sublinear optimization to both space and time complexity.
The rule of thumb is to lazily defer computations if they might happen, but eagerly do computations if they are designed to take place in all times.
Comments