Lambda expressions added a different dimension to the object-oriented Java language. The paradigm I am talking about is functional programming. In this article, we will learn about lambda expressions introduced to the language in Java 8.
Every java developer should learn lambda expressions in order to write more precise, elegant and readable code where applicable. After reading this article you should be able to decide why lambda expressions were most wished for feature, when to use lambda expressions, how to use it effectively and when to avoid it. You should also be able to teach lambda expressions to your team members and colleagues!
Before diving deeper into lambda expressions in java, I want to make you familiarized with basics of functional programming and how that fits into the broader programming landscape. Little bit context will make you more interested in learning the concepts of lambda expressions.
Brief Introduction to Programming Paradigms
Programming is the way of instructing a computer to perform specific tasks that fulfill some definite objectives intended by the programmer. Programming paradigm is the style or way of giving that instruction.
All programming styles are not suited for all types of tasks. Also, there is no one to one relationship between programming paradigm and programming language. One programming language may support multiple programming styles. More programming paradigms a language supports, more options and liabilities it offers to the programmers.
Now, what are the most common programming paradigms? Most used programming paradigms are:
- Imperative: In imperative programming we instruct the computer each step needs to be performed in specific order to achieve a desired result. This includes both execution steps as well as control flow logic. In imperative style of programming we dictate how the work is to be done. Imperative style of programming is very low level. Therefore, programmer has more control on how to optimize or fine tune the algorithm. However, this comes at the cost of more verbose coding, lower re usability and maintainability.
- Procedural: Procedural languages follow imperative style of programming but improves re usability by allowing us create functions or procedures to write reusable codes. So, a programmer is allowed to invoke functions in a statement. C is a popular procedural language.
- Object Oriented: Object oriented programming also shares imperative programming philosophy but further improves re usability and encapsulation. So, both attributes and behaviors can be reused between objects by virtue of inheritance. Examples of popular languages that support OOP are Java, C#, Python etc.
- Declarative: In declarative style of programming, programmers declare properties of the expected outcome, but not how to reach that outcome. The details of the actual steps followed to get the outcome is handled by a separate system. Declarative programming is high level programming as the programmer declares the expected outcome in a language close to human readable English. SQL is an example of declarative language where the programmer states the expected results in term of queries with certain conditions along with grouping and ordering requirements.
- Functional: Functional programming also follows declarative style of programming where expected outcome is expressed as a series of function applications. Functions are the building blocks of functional programming. Functional style of programming discourages use of global mutable variables to hold states of a system. The functions normally take one or more arguments and returns a value. It does not alter the state of input arguments. Functions can also be passed as arguments to other function call. As it does not depend on the state of global variables, same inputs always produce the same outputs. A good example of languages that support functional programming is Haskell and Scala.
Why Functional Programming?
Now that we have some understanding about different programming styles, let us focus on functional programming which is the topic of this article. I have already given some idea about functional programming in the above section. But why should we bother about functional programming in the first place? What are the benefits we get from functional programming?
- Readability of codes written in functional programming are better as statements are written as series of chained method calls with relevant names (code examples provided in later sections of the article will make it clearer).
- As there are no global variables, chances of multiple threads updating same variable and threads referencing old state of a variable are avoided in functional programming. So, it is much easier to write thread safe applications. Side effects of mutable state is avoided in functional programming.
- As we are increasingly using machines with multiple cores, supporting concurrent processing is no longer a nice to have feature. This is a must for new age applications. This is a very compelling reason of using functional programming. In java, streams API along with lambda expressions helps us achieve that goal.
Java and Functional Programming
Java has been one of most popular object-oriented programming languages for a long time. Java 8 release was the biggest release in terms of new features added to the language after generics were introduced in Java 5.
Java 8 was huge because it added functional programming features to the language by introducing lambda expressions, method references and streams API. Lambda expressions allow us implement interfaces with single methods more precisely. We will discuss about them in more detail in the following sections.
Now you may say you have used anonymous inner classes before which also supported functional programming to some extent. Yes, but the verbosity of java made it difficult to write functional code effectively. This is specifically true if the interface we are implementing in the inner class has only one method.
An object with only one method and no instance variables can be considered to encapsulate some specific behavior. When we need to pass behavior or functionality as method argument, such as event handlers for GUI programming, we should be able to use a much simpler syntax. Lambda expressions enable us to do this and the interfaces with only one method we are talking about are called functional interfaces.
RowMapper interface of Spring framework is another good example. RowMapper interface is used by Spring JdbcTemplate for mapping rows of a ResultSet to a specific object for each row returned. RowMapper interface has only one method mapRow(java.sql.ResultSet rs, int rowNum). JdbcTemplate has a query() method which expects an implementation of RowMapper interface.
Most of the times we don’t need to reuse RowMapper implementations when the result set of the database queries are different. Therefore, we normally use an anonymous class implementing mapRow() method and pass that as argument to the query() method. Please refer to the code examples below where we have fetched a Vehicle object by its id:
public Vehicle findByIdWithoutLambda(long id) {
return jdbcTemplate.queryForObject(
FIND_BY_ID,
new RowMapper<Vehicle>() {
@Override
public Vehicle mapRow(ResultSet rs, int i) throws SQLException {
Vehicle vehicle = new Vehicle();
vehicle.setId(rs.getLong("id"));
vehicle.setVehicleType(rs.getString("type"));
vehicle.setMake(rs.getString("make"));
vehicle.setModel(rs.getString("model"));
vehicle.setNumberOfWheels(rs.getInt("number_of_wheels"));
vehicle.setEngineVolume(rs.getDouble("engine_volume"));
vehicle.setNumberOfSeats(rs.getInt("number_of_seats"));
vehicle.setNumberOfDoors(rs.getInt("number_of_doors"));
vehicle.setTransmissionType(rs.getString("transmission_Type"));
vehicle.setFuelType(rs.getString("fuel_type"));
return vehicle;
}
},
id
);
}
First thing we notice about this code is that it is too verbose and not very readable. Also there are lot of boilerplate code which could be inferred by the compiler. Lambda expressions in java 8 provides a more concise syntax for writing such single method interface implementations. Please check following code example where we have rewritten the RowMapper using lambda expressions.
public Vehicle findById(long id) {
return jdbcTemplate.queryForObject(
FIND_BY_ID,
(rs, rowNum) -> {
Vehicle vehicle = new Vehicle();
vehicle.setId(rs.getLong("id"));
vehicle.setVehicleType(rs.getString("type"));
vehicle.setMake(rs.getString("make"));
vehicle.setModel(rs.getString("model"));
vehicle.setNumberOfWheels(rs.getInt("number_of_wheels"));
vehicle.setEngineVolume(rs.getDouble("engine_volume"));
vehicle.setNumberOfSeats(rs.getInt("number_of_seats"));
vehicle.setNumberOfDoors(rs.getInt("number_of_doors"));
vehicle.setTransmissionType(rs.getString("transmission_Type"));
vehicle.setFuelType(rs.getString("fuel_type"));
return vehicle;
},
id
);
}
Above style of coding is much more concise and removes lot of boilerplate coding. You will notice that there is a completely new syntax used in this code.
- There is no method name specified.
- Method parameters are specified without specifying the type for them.
- An arrow symbol is used between the method body and the method parameters.
So the parts of the code that can be inferred by the compiler are no longer needed when we are using lambda expressions.
Java compiler could do this because the functional interface has only one method. Essentially, we have passed a method implementation as an argument to jdbcTemplate.queryForObject() method. With lambda, functions have got the status of first class objects that can be passed as input where objects were allowed earlier. For Java users this is certainly a welcome change.
The magic does not stop with the lambda expressions. Java 8 also introduced method reference to make the code more precise. I will explain about method references in a separate article.
Another frequently used scenario for lambda expression is for comparing and sorting list of objects. Java provides popular Comparator interface for comparing objects when working with list of objects.
Before going to the code examples on sorting, let us see how lambda expression improves comparator code. We will continue with the Vehicle class. Following code example shows the code for comparator where two vehicles are compared by their make.
//Writing comparator without Lambda
Comparator<Vehicle> compareVihiclesByMake = new Comparator<Vehicle>() {
@Override
public int compare(Vehicle o1, Vehicle o2) {
return o1.getMake().compareTo(o2.getMake());
}
};
Not let us rewrite the code using lambda expression:
Comparator<Vehicle> compareVehiclesByMakeLambda =
(o1, o2) -> o1.getMake().compareTo(o2.getMake());
This Comparator object can be passed to Collections.sort() method or sort() method of the list object (implementation of List interface).
Now as Comparator has only one method compare(), this can be used as a functional interface. So, lambdas can be used where an implementation of Comparator is expected.
Now you will see how lambda for Comparator can be used to sort objects.
//Create some random vehicles
Vehicle toyotaCorolla = new Vehicle("SEDAN","TOYOTA", "COROLLA", "DIESEL");
Vehicle fordFigo = new Vehicle("HATCHBACK","FORD", "FIGO", "DIESEL");
Vehicle toyotaFortuner = new Vehicle("SUV","TOYOTA", "FORTUNER", "DIESEL");
Vehicle fordAspire = new Vehicle("SEDAN","FORD", "ASPIRE", "DIESEL");
Vehicle toyotaEtios = new Vehicle("SEDAN","TOYOTA", "ETIOS", "DIESEL");
Vehicle fordEndevour = new Vehicle("SUV","FORD", "ENDEVOUR", "DIESEL");
//Make a list of vehicles
List<Vehicle> vehicleListUnsorted = Arrays.asList(toyotaCorolla,fordFigo,toyotaFortuner,fordAspire,
toyotaEtios,fordEndevour);
//Sorting Without Lambda
//Vehicle list before sort
System.out.println();
System.out.println("*************Unsorted Vehicles************************");
System.out.println();
vehicleListUnsorted.forEach((vehicle -> System.out.println(vehicle)));
//Sort By Make
vehicleListUnsorted.sort((Vehicle o1, Vehicle o2) -> o1.getMake().compareTo(o2.getMake()));
System.out.println();
System.out.println("*************Vehicles Sorted By Make******************");
System.out.println();
vehicleListUnsorted.forEach((vehicle -> System.out.println(vehicle)));
When we run the code following output is shown in the console:
We could also use Collections.sort() method to achieve the same result. However, sort() method of the list object looks more intuitive.
Collections.sort(vehicleListUnsorted, (Vehicle o1, Vehicle o2) -> o1.getMake().compareTo(o2.getMake()));
That is all for this post. In the sequel of this post we will discuss in detail about how lambda expressions can be used (syntax detail), in built functional interfaces in Java, type inference, best practices for lambda usage and a bit about Streams API in java. So stay tuned.