Understanding Java Lambda Expression: Why It Needs an Interface
Introduction
Java introduced lambda expressions in Java 8, even though it is a statically-typed programming language. Lets explore what is lambda expression and how Java adopted functional programming.
What is lambda expression?
A lambda expression also known as an anonymous function or lambda function, is a way to define small & unnamed functions in programming. A ->
or =>
(depending on the language) uses to separate the function parameters from function body.
|
|
Lambda function can be assigned to a variable, can also be passed to another functions parameter or can be returned from a function; just treated like an another variable. These properties makes it First-Class function. Also a lambda functions parameter type and return type may not be defined.
|
|
Lambda Expression in Java
Java is a statically-typed language, means the data types of variables and expressions must be known and defined at compile time. The compiler strictly checks the type compatibility if something goes wrong it won’t compile. In dynamically typed languages (Javascript, Python etc), the data type is evaluated in runtime. This caused an issue for Java with lambda as lambdas are expected to have no declared data type.
|
|
As an OOP programming language, everything in Java is Objects. Even if we need only one method, we need to declare it in a class, instantiate it and use it. For example Collections.sort accepts an instance of Comparator interface, although it only has one method compare
.
|
|
What if we can just use the compare
method without unnecessary implementing a new class, a single function!
|
|
Now come to the Function
type. Java’s only functions are class methods which are tightly dependant on the class it is declared in. Lets say we have declared type of arguments, but what will be the type here when we assign the function to a variable addFunc
?
|
|
Java came up with Single Abstract Method (SAM) interface. Any interface that has only a single method is called SAM. It is also known as functional interfaces because they can be used to represent functions or actions.
A functional interface defines a lambda function, how many parameter it should have as well as the entire lambda function signature.
|
|
The method signature int add(int a, int b);
in the interface exactly matched with the lambda (a, b) -> a + b
and it can be assigned the lambda variable addFunc
as AdderFunction.
As you can see, we can not just directly invoke the function addFunc(3, 5)
. We need to call the method addFunc.add(..)
that declared in the interface. We need to access the method via interface. Whenever we write an lambda expression, there must be an interface declared for it. Because lambda expressions are still treated as an Object in Java. Java just gives an way to eliminate the extra code as well as using the shorter & concise lambda expressions. But its not the only benefits of it, it also gives Java to make use of functional programming principles like pure functions, First-Class functions, Higher-Order functions etc.
As we told that lambdas are treated as an Object in Java, we might think Java just implement an anonymous class under the hood when its compiled. Thats not entirely true, we will discuss that in another article.
Built-In Functional Interfaces
As every unique lambda expression in Java needs an interface, Java provides some useful built in interfaces under java.util.function package to reuse. Lets take a look in some of these:
Function<T, R>: Accepts one argument and produces a result, method R apply(T t);
. Stream.map uses it as parameter: list.stream().map(c -> c.getFirstName())
.
Predicate< T >: Accepts one argument and returns boolean, method boolean test(T t);
. Stream.filter uses it as parameter: list.stream().filter(c -> c.getAge() > 10)
Consumer< T >: Accepts a single input argument and returns no result, its use to modify the argument, method void accept(T t);
. Iterable uses it for forEach
looping: list.forEach(c -> System.out.println(c))
Supplier< T >: Accepts no argument and returns a result, its uses to modify the argument, method void accept(T t);
. Optional.orElseGet uses it Optional.of(null).orElseGet(() -> "N/A")
.
Advantages of Lambda in Java
We already discussed how lambdas help Java to achieve functional programming principles. Using lambda, we can create Pure functions; Functions that always return the same output for the same input. Unlike a class method, they don’t modify external state or variables and produces no side effects.
The elimination of writing verbose code with anonymous inner classes for callbacks or passing functions as arguments makes code more concise and readable.
Java 8 introduced Stream API, which take advantages of lambda to work with collections in a functional style. This makes operations like filtering, mapping, reducing more expressive and easier parallelization.
Many other modern programming languages, have adopted lambda expressions functional programming features. In Java Specification Requests stated that: There is growing interest in running a variety of programming languages on the the Java platform, and consequently, on the Java virtual machine (JVM). Lambda feature keeps Java competitive and attractive to developers.
Conclusion
The introduction of lambda expressions in Java marks a significant shift toward functional programming principles. This transition allows evolution of Java as a versatile language in modern software development. In next article, we will discuss about the internal working of lambda expression in JVM.