Java is a very popular programming language widely used to build software applications works on the principle of object-oriented programming, in which software applications are built around objects, which are software entities that combine both data and behaviour into a single unit.
The collection framework in Java is a powerful, unified architecture that provides standard interfaces like list, map, set and ready-made classes such as ArrayList, HashSet, and HashMap for storing, manipulation, and retrieving groups of objects efficiently.
In this article, we’ll cover the topic of functional interfaces in Java 8. A functional interface is a special type of interface in programming that has exactly one abstract method, allowing it to represent a single unit of computation, often used for lambda expressions and method references to write cleaner and more concise code.
Key Characteristics of Functional Interfaces in Java
Functional Interfaces provide a target type for lambda expressions and method references which significantly improves code conciseness, readability and allows Java to incorporate concepts of functional programming. Given below is the list of key characteristics of functional interfaces in Java.
- Single Abstract Method (SAM): It is the core concept behind functional interface which were introduces in Java 8 to support lambda expressions. It is important because it treat behaviour as data and reduce boilerplate code.
- Default and Static Methods: These methods were introduced in interfaces to support functional programming while maintaining backward compatibility.
- Object Methods: Object methods are methods inherited from the java.lang.Object class that allows the functional interfaces to be treated as a single abstract method type.
Why Were Lambda Expressions Introduced?
Lambda expressions in Java (introduced in Java 8) provide a concise way to represent anonymous functions, primarily used to implement functional interfaces. Before the introduction of Java 8, implementing interfaces often required a verbose anonymous inner class. This made the code harder to read and maintain.
Given below is the list of features provided by lambda expressions, which made the coding part easier.
- Reduce boilerplate code
- Improve readability
- Support functional programming
- Enable easy use of Streams API
- Write more expressive and cleaner code
Functional Interfaces: The Backbone of Lambda Expressions
Functional Interfaces act as the contract that the lambda expression fulfils. In the absence of this, the specific structure, concise syntax and type inference of lambda expressions would not be possible. Given below is an example of functional interfaces.
Example
@FunctionalInterface
interface Greeting {
void helloWorld();
}
Simple Lambda Expression Examples
1. No Parameters
Given below is an example of a simple lambda expression with no parameters.
Greeting great = () -> System.out.println("Welcome to TpointTech");
great.helloWorld();
A lambda expression with no parameters is represented by a pair of empty parenthesis () followed by the lambda operator -> and then body of the expression or a code block.
2. Single Parameter
A simple lambda expression with a single parameter can be written concisely by omitting the parameter type and the parentheses around the parameter. Given below is an example of lambda expression with a single parameter.
@FunctionalInterface
interface Square {
int calculate(int x);
}
public class Main {
public static void main(String[] args) {
Square s = x -> x * x;
System.out.println(s.calculate(6)); // Output: 36
}
}
3. Multiple Parameters:
The core difference between single-parameter and multiple-parameter lies in their syntax and the number of inputs that the anonymous function accepts. Given below is an example of a lambda expression with multiple parameters.
@FunctionalInterface
interface Add {
int sum(int a, int b);
}
public class Main {
public static void main(String[] args) {
Add add = (a, b) -> a + b;
System.out.println(add.sum(12, 24)); // Output: 36
}
}
Lambda Expressions with Collections
In Java, collections are the standard data structures used to store and manipulate groups of objects. Examples of collections include list, set and map. Key benefits of using lambda expressions with collections include.
- Conciseness and Readability: The collections reduced boilerplate code, making the code shorter and easier to understand. It also provide clear intent of what operation is being performed on the collection element.
- Enhanced Functionality and Flexibility: Lambda expression revolutionized developer work by transferring the programming language from a verbose, imperative style to a more concise, readable and declarative function style. Lambda expressions when combined with collections and the Stream API provide enhanced functionality through conciseness, readability and powerful data manipulation capabilities.
- Enhanced API Usability with the Stream API: Lambda expressions works seamlessly with the Java Streams API, enabling powerful and flexible operations like filtering, mapping, sorting, and reducing data through functional interfaces, declarative style, internal iteration, and intermediate operations in a declarative manner.
- Support for Parallel Processing: Lambda expressions with collections enabling parallel processing by working with the Java 8 Streams API, which handles the complexities of multithreading automatically. This efficiently utilizes multi-core CPUs for bulk operations, leading to potential performance improvements.
Given below is an example of iterating over lists using a Method Reference
Example
List<String> names = Arrays.asList(“Java”, “Python”, “C++”);
names.forEach(name -> System.out.println(name));
Lambda Expressions with Comparator
Lambda expressions provide a consise way to implement the comparator interface (used to define custom sorting logic for objects) that helps developers to express the comparison logic in a single, readable line of code instead of creating a separate class or writing verbose, anonymous inner classes. Given below is the syntax for lambda expression used as a comparator.
Syntax
(o1, o2) -> { return comparison_logic; }
Where o1 and o2 are two objects being compared by the comparator.
Given below is an example of sorting objects in a collection.
Example
class Student {
String name;
int marks;
Student(String name, int marks) {
this.name = name;
this.marks = marks;
}
}
List<Student> students = new ArrayList<>();
students.add(new Student("Rahul", 78));
students.add(new Student("Suraj", 64));
students.add(new Student("Ayush", 86));
students.sort((s1, s2) -> s2.marks - s1.marks); // descending order
Lambda Expressions with Streams API
The Stream API and lambda expressions work together as the Stream API provides a pipeline to process collections such as filtering, mapping, sorting and aggregating, whereas Lambda expressions provide the behavior (logic) that the Stream API applies to each element. In short streams defines what to do with data and Lambda define how to do it. They work together to provide a powerful, functional-style approach to processing collections of data.
Given below is an example of Lambda Expressions with Stream API to filter even numbers and print them.
Example
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
numbers.stream()
.filter(n -> n % 2 == 0)
.forEach(System.out::println);
Limitations of Lambda Expressions
Lambda expressions have made the Java code cleaner and more expressive, but they also come with certain limitations. Given below is the list of limitations of lambda expressions in Java.
1) Can be used only with Functional Interfaces
Lambda expressions work only with functional interfaces which means interfaces having exactly one abstract method. It cannot work with abstract classes or interfaces with multiple abstract methods. Given below is the implementation of lambda expressions through functional interfaces.
Runnable r = () -> System.out.println("Running");
2) No Explicit Method Name
Lambda expressions are anonymous function which means they do not have a method name, return type or access modifier. This makes them harder to debug and difficult to reuse. They are designed to provide a concise, inline implementation of a functional interface, which means an interface with a single abstract method.
3) Limited to One Implementation
Limited to one implementation means lambda expression can only provide the body for a single method which must belong to a functional interface. A lambda provide only one behavior and if complex logic is required, it reduces the readability.
4) Cannot Modify Local Variable
Lambda expression cannot modify a local variable from its enclosing scope because the variable must be final or effectively final. Given below is an example to demonstrate this.
int x = 10;
x = 20;
list.forEach(n -> System.out.println(x)); // compile-time error
This restriction exists to maintain thread safety.
Conclusion
Lambda expressions reduce boilerplate code and improve readability, but they are limited to functional interfaces, cannot modify local variables, handle checked exceptions poorly, and are unsuitable for complex logic or stateful operations. Mastering functional interface is essential for any Java developer aiming to write modern, efficient and scalable code.
Note: You can keep both of the below links nofollow, or you can ignore also if you don’t want to mention the Author BIO (it can give benefit to meet EEAT).
Written By: Sudhir Sharma (Published By Henery James)
Sudhir Sharma is an SEO specialist, technical content creator, and the founder of IncludeHelp.com, a popular programming tutorial platform. He writes about programming, software development, and digital marketing, helping learners and professionals understand complex technical concepts in a simple way.
LinkedIn: Sudhir Sharma