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 ints 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?

If you’re interested in learning more, why not check out our online course, Introduction to Java 8, which gives a comprehensive guide covering lambda expressions, functional interfaces, pipelines and streams and a whole lot more!