Lambda Expressions in Java
This article introduces you to Lambdas in Java, how are they made, what use they are of and what purpose do they actually achieve. We characterised Lambdas based on their syntax, what variations in code they can handle and how they are different from Anonymous Inner classes
We also compared Java 8 Lambdas to Closures. Now, let’s dive in.
Lambdas
Java lambda expressions are one of the newest feature introduced in Java 8. Lambda expressions are Java's first step into functional programming.
>>> Lambda expression is a function which can be created without belonging to any class.
A lambda expression can be passed around as if it was an object and executed on demand.
Syntax is:
arguments -> body
where arguments can be either
- ()
- a single variable if the type of that variable can be inferred from the context
- a sequence of variables, with or without types, in parentheses
and body can be either an expression or a {...} block with statements. The expression is simply returned, i.e. () -> 2 is equivalent to () -> {return 2;}
To start with an example, let’s look how we can sort Strings in older versions of Java:
List<String> names = Arrays.asList("discover", "sdk", "java", "lambda");
Collections.sort(names, new Comparator<String>() {
@Override
public int compare(String a, String b) {
return b.compareTo(a);
}
});
With introduction of Lambdas, code can become much cleaner and easier to read as well. Here is the snippet with Lambdas:
Collections.sort(names, (String a, String b) -> {
return b.compareTo(a);
});
As you can see, the code snippet is much cleaner and easier to read. But it can get even shorter:
Collections.sort(names, (String a, String b) -> b.compareTo(a));
Characteristics for Lambdas
- Optional type declaration − Parameter type declaration is not required. The compiler can infer the type from the value of a parameter. Here is an example showing the same:
(arg) -> System.out.println("One parameter: " + arg);
Notice the parameter is listed inside the parentheses.
- Optional parenthesis around parameter − No need to declare a single parameter in parenthesis. For multiple parameters, parentheses are required.
arg -> System.out.println("One parameter: " + arg);
- Optional curly braces − No need to use curly braces in expression body if the body contains a single statement.
- Optional return keyword − The compiler automatically returns the value if the body has a single expression to return the value. Curly braces are required to indicate that expression returns a value. Here is a code fragment indicating the same:
(arg) -> {
System.out.println(“arg: “ + arg);
return "return value";
}
It becomes shorter:
(a1, a2) -> { return a1 > a2; }
Even more shorter,
(a1, a2) -> a1 > a2;
- Multiple parameters can be written inside the parentheses as well, without type, as shown:
(p1, p2) -> System.out.println("Multiple parameters: " + p1 + ", " + p2);
- Lambda function can also have a body. We can use curly braces to define a body containing multiple statements. Let’s look at a code fragment for the same:
(previous, newer) -> {
System.out.println("Previous: " + previous);
System.out.println("Newer: " + newer);
}
Use “Effectively Final” Variables
If we try to access a non-final variable inside a lambda expressions will cause a compile-time error. But it doesn’t mean that we should mark every target variable as final.
According to the “effectively final“ concept, a compiler treats every variable as final, as long as it is assigned only once.
It is safe to use such variables inside lambdas because the compiler will control their state and trigger a compile-time error immediately after any attempt to change them.
For example, the following code fragment won’t compile:
public void method() {
String localVariable = "Local";
Foo foo = parameter -> {
String localVariable = parameter;
return localVariable;
};
}
The compiler will shout at you as:
Variable 'localVariable' is already defined in the scope.
This approach simplify the process of making lambda execution thread-safe.
Java8 Lambdas vs Anonymous classes
- An anonymous inner class (AIC) can be used to create a subclass of an abstract class or a concrete class. An AIC can also provide a concrete implementation of an interface, including the addition of state (fields). An instance of an AIC can be referred to using this in its method bodies, so further methods can be called on it, its state can be mutated over time, etc. None of these apply to lambdas.
- Majority of uses of AICs were to provide stateless implementations of single functions and so can be replaced with lambda expressions, but there are other uses of AICs for which lambdas cannot be used. AICs are here to stay.
- AICs introduce a new scope. That is, names are resolved from the AIC's superclasses and interfaces and can shadow names that occur in the lexically enclosing environment. For lambdas, all names are resolved lexically.
- Lambdas interfaces can contain only a single abstract method. It would fail as soon as your interface contains more than 1 abstract method. That is where anonymous classes will be useful.
- Anonymous classes can be directly annotated with the new Java 8 Type Annotations, for example:
new @MyTypeAnnotation SomeInterface(){};
Above is not possible with lambda expressions.
Apart from being syntactical sugar nature of Lambdas in Java, they also seems to differ in performance side:
- AICs have a slightly slower performance on first use, but it seems negligible.
- Lambdas have a slower performance when called often because of the bigger stack they build up when called.
This means Lambdas and Inner classes have use-cases as when to use what:
- Use Lambdas for readability when a few milliseconds performance loss is not a problem (GUI stuff, etc.)
- Use inner classes for often called, performance critical functions.
Let’s look at an example here using Collections, we’ll let you decide which one is easier to understand and read, AIC or Lambdas. What's easier to read:
myCollection.map(new Mapper<String,String>() {
public String map(String input) {
return new StringBuilder(input).reverse().toString();
}
});
Or:
myCollection.map(element -> new StringBuilder(element).reverse().toString());
or (using a method handle instead of a Lambda):
myCollection.map(String::toUpperCase);
Are Java 8 Lambdas Closures?
Lambdas and Closures are present to aid functional programming. Java 8 is not a functional programming language, but (like Python) now has some strong support for functional programming on top of its basic OOPs paradigm.
The core idea of functional programming is that we can create and manipulate functions, including creating functions at runtime. Thus, functions become another thing that your programs can manipulate (instead of just data). This adds a lot of power to programming.
>>> Before Java 8, the only way to create functions at runtime was through bytecode generation and loading (which is quite messy and complex).
Lambdas provide two basic features:
- More succinct function-creation syntax.
- The ability to create functions at runtime, which can then be passed/manipulated by other code.
Closures concern this second issue.
>>> Just to add, a closure uses variables that are outside of the function scope.
Conclusion
In this article, we introduced you to Lambdas in Java, how are they made, what use they are of and what purpose do they actually achieve. Also, We had a look at when not to use them which is a major concern. Use Lambdas well to write cleaner and precise code. We compared Lambdas and Inner classes and identified if Lambdas and Closures are close to each other in their concept.
Let’s use this new feature to build better apps!
Recent Stories
Top DiscoverSDK Experts
Compare Products
Select up to three two products to compare by clicking on the compare icon () of each product.
{{compareToolModel.Error}}
{{CommentsModel.TotalCount}} Comments
Your Comment