Introducing lambda expressions for functional programming in Java
In this article we’ll take a look at lambda expressions – the powerhouse behind Java’s functional programming!
Format of Lambda Expressions
The syntax of lambda expressions is very intuitive, easy to understand and apply.
A lambda expression is made up of:
- input parameters
- arrow operator
- expression/statements block
So it’s general format is like this:
<input-params> -> <function-code>
Example Lambda Expressions
Lambda expressions are best shown by examples, so let’s look at a few here.
Takes no inputs, returns no output
The most basic lambda expression will take no inputs and return no output – it’s literally just a block of code which is executed without any context ‘as is’:
() -> System.out.println("Here I am!");
The ()
at the start, in the input parameters position, denotes that there are no parameters being passed (just like the empty parentheses which follow a method which has no parameters).
NOTE: Even though we don’t supply an input parameter, we still need to represent that fact, and so this is why we still always need to put something there – hence the empty parentheses).
Takes a single input, returns no output
To get the lambda expression to take a parameter, we place that parameter in the input parameters position:
name -> System.out.println("Here you are too, " + name + "!");
NOTE: Where we have a single input paraneter, we can omit the parentheses. We can also write (name)
too and this would be allowed
Takes multiple inputs, returns no output
Where we pass multiple input parameters to the lambda expression, we must:
- enclose the parameters in parentheses
- comma-separate them
(name, food) -> System.out.println("So " + name + " enjoys eating " + food + "... interesting!");
Contains multiple statements in the code section
While it’s good practice to keep lambda expressions to single-line expression-like statements, it is possible to have multiple lines in the code section:
() -> {
System.out.println("The owl and the pussycat went to sea");
System.out.println("in a beautiful pea green boat");
}
Return types are never specified
As you may have noticed already, return types are not specified when we define lambda expressions.
So for example, this lambda expression:
() -> System.out.println("I don't return anything!")
and this lambda expression:
() -> "I return this String!"
both have the same look about them. i.e. nothing immediately jumps out syntactically at you that the 2nd lambda returns a string.
Just bear this in mind when using them.
Of course, the compiler knows the difference through the notional use of function descriptors
Type inference is applied automatically where possible
The compiler will use type inference to work out what they types are by looking at the execution context of the lambda expression.
So, in general, you don’t have to worry about casting to a specific type.
Function descriptor notation
When thinking about which types we can assign a lambda expression to, function descriptors are a useful tool.
A function descriptor is basically the method signature that a lambda expression (or method) exposes.
The syntax is almost the same as for a lambda expression, except instead of having a code section, we have an output type section instead.
So the general format for a function descriptor is this:
<input-parameter-types>
-> <output-parameter-type>
Example function descriptors
Here are some sample function descriptors:
Function descriptor | Description |
---|---|
() -> () |
A method which takes no input parameters, and returns no value |
(String) -> () |
A method which takes an input parameter, and returns no value |
() -> (int, float) |
A method which takes no input parameters, and returns an int and a float |
(int[]) -> (SortedMap<Character, Integer>) |
A method which takes an array of int s and returns a SortedMap of Character to Integer |
Having function descriptors in our heads will allow us to think better about type compatibility when considering which target types we might use for a lambda expression.
We’ll see more of this in another follow up blog article when we cover functional interfaces. For now though, just think of it as a quick way of denoting a method signature.
Wrap Up
As you can see, lambda expressions are a neat and tidy way of encapsulating behaviour in an easy to pass way, forming the heart of the functional programming paradigm brought to Java with the JDK 8 release!
Where Next?
💥 Want more? Go get the FULL COURSE here: 👉 Mastering Java 8 Fundamentals 💥
►► Grab our FREE beginners guide to Java if you’re completely new to Java!
👉 Check out our courses at https://courses.javaeasily.com/courses
👉 Listen to our podcast at https://spotifyanchor-web.app.link/e/xjAOXisFABb
👉 Visit out our website at https://javaeasily.com/
👉 Read our articles on Medium at https://medium.com/java-easily
You can also join in the conversation by connecting with us at our Facebook page or alternatively follow us on Twitter!