“If you’re diving into Java 8, terms like functional interface, lambda expression, and method reference might seem daunting. It can feel like a ‘snake swallowing its own tail’—you start learning one concept, only to be pulled into another, and soon you’re caught in a cycle of definitions that all sound the same. Many tutorials focus on abstract ideas like code re usability and readability, but without practical examples, these concepts can feel vague and disconnected.”
“Don’t worry, though—if you’re new to Java and want a clear, straightforward explanation, you’ve come to the right place. While functional interfaces, lambda expressions, and method references are the highlights of Java 8, their real purpose is to make Java code more functional, efficient, and maintainable. Here’s a breakdown of what each of these features really aims to achieve:”
Facilitate Parallel Processing: The Stream API provides built-in support for parallel processing, allowing easy parallelism with a functional style (parallelStream()).
Reduce Boilerplate Code: Lambdas and method references allowed developers to write less code to accomplish the same tasks.
Enhance Readability and Maintainability: The functional style makes the code more concise and often easier to read.
To understand the need for functional programming, let’s take a step back in time—before Java 8. Let’s see how we used to filter and sort movie genres back then.
Setting up the movie class
public class Movie {
private String title;
private String genre;
private double rating;
public Movie(String title, String genre, double rating) {
this.title = title;
this.genre = genre;
this.rating = rating;
}
public String getTitle() {
return title;
}
public String getGenre() {
return genre;
}
public double getRating() {
return rating;
}
@Override
public String toString() {
return title + " (" + genre + ", Rating: " + rating + ")";
}
}
Sample movie list
List<Movie> movies = Arrays.asList(
new Movie("Movie A", "Action", 8.5),
new Movie("Movie B", "Drama", 7.8),
new Movie("Movie C", "Action", 9.2),
new Movie("Movie D", "Comedy", 6.3),
new Movie("Movie E", "Action", 7.5)
);
1. Filtering Movies by Genre by Java 7
Non Functional due to Mutable State, Explicit Loops, Procedural Control Flow
public static List<Movie> filterMoviesByGenre(List<Movie> movies, String genre) {
List<Movie> result = new ArrayList<>();
for (Movie movie : movies) {
if (movie.getGenre().equalsIgnoreCase(genre)) {
result.add(movie);
}
}
return result;
}
// Usage
List<Movie> actionMovies = filterMoviesByGenre(movies, "Action");
for (Movie movie : actionMovies) {
System.out.println(movie);
}
- Mutable State: The method creates a mutable
ArrayList(result) and adds to it usingresult.add(movie). Functional programming encourages immutability, where data is not modified once created. - Explicit Loops: The method uses an explicit
forloop to iterate over themovieslist, which is more characteristic of an imperative style. Functional programming tends to rely on declarative constructs like streams and lambda expressions, which abstract away explicit loops. - Procedural Control Flow: The logic here is imperative in nature—you’re specifying how to perform the filtering with a loop and an
ifstatement. In functional programming, you’d typically describe what you want to do, not how to do it.
2. Sorting Movies by Rating in Descending Order by Java 7
Non functional due to Explicit Sorting with a Comparator, Mutable State, Imperative Control Flow:
public static List<Movie> sortMoviesByRating(List<Movie> movies) {
List<Movie> sortedMovies = new ArrayList<>(movies);
Collections.sort(sortedMovies, new Comparator<Movie>() {
@Override
public int compare(Movie m1, Movie m2) {
return Double.compare(m2.getRating(), m1.getRating());
}
});
return sortedMovies;
}
// Usage
List<Movie> sortedMovies = sortMoviesByRating(movies);
for (Movie movie : sortedMovies) {
System.out.println(movie);
}
- Explicit Sorting with a Comparator: You’re explicitly creating a new
Comparatorand usingCollections.sort()to perform the sorting. Functional programming often uses lambda expressions and built-in functional interfaces to simplify operations like sorting. - Mutable State: The method creates a mutable
ArrayList(sortedMovies) to store the sorted results. Functional programming encourages immutability, meaning you should avoid modifying existing data structures. - Imperative Control Flow: The sorting is done via an imperative approach that explicitly describes how the sorting should happen using
Collections.sort()and theComparator. Functional programming emphasizes describing what you want to do instead of how you do it.
1. Filtering Movies by Genre by Java 8
Functional approach to cover Immutability, Declarative Style, Functional Constructs
List<Movie> actionMovies = movies.stream()
.filter(movie -> movie.getGenre().equalsIgnoreCase("Action"))
.collect(Collectors.toList());
actionMovies.forEach(System.out::println);
- Immutability: The
Streamoperations do not modify the original list but instead return a new list with the filtered movies. - Declarative Style: Instead of manually iterating and adding to a result list, you’re declaring the desired transformation (filtering) and leaving the details of the iteration and collection management to the Stream API.
- Functional Constructs: The
filtermethod is a higher-order function that takes a lambda expression to define the filtering criteria. Thecollectmethod is used to gather the filtered results into a new list.
2.Sorting Movies by Rating in Descending Order by Java 8
Functional approach :
List<Movie> sortedMovies = movies.stream()
.sorted((m1, m2) -> Double.compare(m2.getRating(), m1.getRating()))
.collect(Collectors.toList());
sortedMovies.forEach(System.out::println);
- Stream: The
stream()method creates a stream from the list of movies, allowing for a series of functional transformations. - Lambda Expression: The
sorted()method uses a lambda expression(m1, m2) -> Double.compare(m2.getRating(), m1.getRating())to define how movies should be sorted based on their rating. - Immutability: Like in the functional example above, the original list is not modified. Instead, the sorted results are collected into a new list using
collect(). - Declarative: This approach focuses on declaring that you want to sort the movies by rating, rather than specifying how to sort them.
Shift Toward Functional Programming
Functional programming promotes immutability, statelessness, and pure functions—making code less error-prone and more predictable. Java 8’s shift towards functional programming allowed developers to embrace a modern coding style without abandoning Java’s OOP foundations.
Conclusion
Java 8’s introduction of functional programming features marked a significant shift in how developers approach coding. By embracing lambda expressions, functional interfaces, and method references, Java combines the best of both worlds—preserving its object-oriented roots while enabling more concise, efficient, and maintainable code. This evolution not only enhances the developer experience but also helps in building modern, scalable applications that can better handle today’s complex programming challenges.
