šŸŽÆ Lambda Expressions and Functional Programming

Transform your Java coding style through functional paradigms, stream processing mastery, and lambda-powered elegant solutions


šŸŽÆ Learning Objectives

By the end of this lesson, you will:


šŸš€ Functional Programming Evolution

<aside> ⚔

Functional Programming Philosophy

Functional programming treats computation as the evaluation of functions - avoiding changing state and mutable data. In Java, this means writing more declarative code that says WHAT you want rather than HOW to do it.

</aside>

The Evolution from Imperative to Functional

Java Functional Evolution - From Verbose to Elegant
═══════════════════════════════════════════════════════════════════════════════

    šŸ“œ JAVA 7 & EARLIER                    šŸŽÆ JAVA 8+ LAMBDA ERA
ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”   ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
│ // Anonymous Inner Classes          │   │ // Lambda Expressions              │
│ Comparator<String> comp = new       │   │ Comparator<String> comp =           │
│   Comparator<String>() {            │   │   (s1, s2) -> s1.length() -        │
│     @Override                       │   │   s2.length();                      │
│     public int compare(String s1,   │   │                                     │
│                       String s2) {  │   │ // Method References                │
│       return s1.length() -          │   │ list.forEach(System.out::println); │
│              s2.length();           │   │                                     │
│     }                               │   │ // Stream API                       │
│   };                                │   │ [list.stream](<http://list.stream>)()                       │
│                                     │   │     .filter(s -> s.length() > 3)   │
│ // Imperative Loops                 │   │     .map(String::toUpperCase)       │
│ List<String> result = new           │   │     .collect(Collectors.toList()); │
│   ArrayList<>();                    │   │                                     │
│ for (String s : list) {             │   │                                     │
│   if (s.length() > 3) {             │   │                                     │
│     result.add(s.toUpperCase());    │   │                                     │
│   }                                 │   │                                     │
│ }                                   │   │                                     │
ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜   ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜

    😰 PROBLEMS:                            ✨ BENEFITS:
    • Verbose boilerplate                   • Concise & readable
    • Mutable state                         • Immutable operations  
    • Error-prone loops                     • Declarative intent
    • Hard to parallelize                   • Easy parallelization

šŸŽ­ Lambda Expression Mastery

Lambda Expression Architecture

<aside> āž”ļø

Lambda Syntax Formula

(parameters) -> expression or (parameters) -> { statements; }

Lambda = Parameters + Arrow Token + Body = Functional Interface Implementation

</aside>

šŸŽ¬ Movie Streaming Platform

public class MovieStreamingPlatform {
    
    // šŸŽ¬ Movie data model
    public static class Movie {
        private String title;
        private String genre;
        private int year;
        private double rating;
        private int duration; // minutes
        private List<String> actors;
        
        public Movie(String title, String genre, int year, double rating, 
                    int duration, List<String> actors) {
            this.title = title;
            this.genre = genre;
            this.year = year;
            this.rating = rating;
            this.duration = duration;
            this.actors = actors;
        }
        
        // Getters
        public String getTitle() { return title; }
        public String getGenre() { return genre; }
        public int getYear() { return year; }
        public double getRating() { return rating; }
        public int getDuration() { return duration; }
        public List<String> getActors() { return actors; }
        
        @Override
        public String toString() {
            return String.format("%s (%d) - %s [%.1f/10, %dmin]", 
                               title, year, genre, rating, duration);
        }
    }
    
    private List<Movie> movies;
    
    public MovieStreamingPlatform() {
        initializeMovies();
    }
    
    private void initializeMovies() {
        movies = Arrays.asList(
            new Movie("Inception", "Sci-Fi", 2010, 8.8, 148, 
                     Arrays.asList("Leonardo DiCaprio", "Marion Cotillard", "Tom Hardy")),
            new Movie("The Matrix", "Sci-Fi", 1999, 8.7, 136, 
                     Arrays.asList("Keanu Reeves", "Laurence Fishburne", "Carrie-Anne Moss")),
            new Movie("Pulp Fiction", "Crime", 1994, 8.9, 154, 
                     Arrays.asList("John Travolta", "Uma Thurman", "Samuel L. Jackson")),
            new Movie("The Dark Knight", "Action", 2008, 9.0, 152, 
                     Arrays.asList("Christian Bale", "Heath Ledger", "Aaron Eckhart")),
            new Movie("Forrest Gump", "Drama", 1994, 8.8, 142, 
                     Arrays.asList("Tom Hanks", "Robin Wright", "Gary Sinise")),
            new Movie("The Godfather", "Crime", 1972, 9.2, 175, 
                     Arrays.asList("Marlon Brando", "Al Pacino", "James Caan")),
            new Movie("Parasite", "Thriller", 2019, 8.6, 132, 
                     Arrays.asList("Song Kang-ho", "Lee Sun-kyun", "Cho Yeo-jeong")),
            new Movie("Spirited Away", "Animation", 2001, 9.3, 125, 
                     Arrays.asList("Rumi Hiiragi", "Miyu Irino", "Mari Natsuki"))
        );
    }
    
    // šŸŽÆ Lambda Expression Syntax Demonstrations
    public void demonstrateLambdaSyntax() {
        System.out.println("šŸŽÆ Lambda Expression Syntax Variations");
        System.out.println("=====================================");
        
        // 1ļøāƒ£ No parameters - Supplier<T>
        Supplier<String> greeting = () -> "Welcome to our movie platform!";
        System.out.println("No parameters: " + greeting.get());
        
        // 2ļøāƒ£ Single parameter (parentheses optional)
        Consumer<String> printer1 = message -> System.out.println("šŸ“¢ " + message);
        Consumer<String> printer2 = (message) -> System.out.println("šŸ“¢ " + message);
        
        printer1.accept("Single parameter without parentheses");
        printer2.accept("Single parameter with parentheses");
        
        // 3ļøāƒ£ Multiple parameters
        BiFunction<String, String, String> combiner = (a, b) -> a + " & " + b;
        System.out.println("Multiple parameters: " + combiner.apply("Action", "Adventure"));
        
        // 4ļøāƒ£ Block body with return
        Function<Movie, String> movieSummary = (movie) -> {
            StringBuilder summary = new StringBuilder();
            summary.append("šŸŽ¬ ").append(movie.getTitle());
            summary.append(" (").append(movie.getYear()).append(")");
            if (movie.getRating() > 8.5) {
                summary.append(" ⭐ HIGHLY RATED!");
            }
            return summary.toString();
        };
        
        System.out.println("Block body example:");
        [movies.stream](<http://movies.stream>)()
              .limit(3)
              .map(movieSummary)
              .forEach(System.out::println);
        
        // 5ļøāƒ£ Explicit type parameters (usually inferred)
        Comparator<Movie> ratingComparator = (Movie m1, Movie m2) -> 
            [Double.compare](<http://Double.compare>)(m2.getRating(), m1.getRating());
        
        System.out.println("\\nTop 3 rated movies:");
        [movies.stream](<http://movies.stream>)()
              .sorted(ratingComparator)
              .limit(3)
              .forEach(System.out::println);
    }
    
    // šŸŽ­ Functional Interface Usage Examples
    public void demonstrateFunctionalInterfaces() {
        System.out.println("\\nšŸŽ­ Functional Interface Patterns");
        System.out.println("================================");
        
        // Predicate<T> - boolean test
        Predicate<Movie> isRecentMovie = movie -> movie.getYear() >= 2010;
        Predicate<Movie> isHighRated = movie -> movie.getRating() >= 8.0;
        Predicate<Movie> isActionMovie = movie -> "Action".equals(movie.getGenre());
        
        // Combine predicates
        Predicate<Movie> recentHighRatedAction = isRecentMovie
            .and(isHighRated)
            .and(isActionMovie);
        
        System.out.println("Recent high-rated action movies:");
        [movies.stream](<http://movies.stream>)()
              .filter(recentHighRatedAction)
              .forEach(movie -> System.out.println("  šŸŽ¬ " + movie));
        
        // Function<T, R> - transformation
        Function<Movie, String> genreExtractor = Movie::getGenre;
        Function<String, String> genreFormatter = genre -> "šŸ“ŗ " + genre.toUpperCase();
        Function<Movie, String> genreDisplay = genreExtractor.andThen(genreFormatter);
        
        System.out.println("\\nGenre transformation:");
        [movies.stream](<http://movies.stream>)()
              .map(genreDisplay)
              .distinct()
              .forEach(System.out::println);
        
        // Consumer<T> - side effects
        Consumer<Movie> movieLogger = movie -> 
            System.out.println("šŸ” Viewed: " + movie.getTitle());
        Consumer<Movie> ratingLogger = movie -> 
            System.out.println("⭐ Rating: " + movie.getRating());
        Consumer<Movie> fullLogger = movieLogger.andThen(ratingLogger);
        
        System.out.println("\\nMovie logging:");
        [movies.stream](<http://movies.stream>)()
              .filter(movie -> movie.getYear() == 1994)
              .forEach(fullLogger);
        
        // BiFunction<T, U, R> - two inputs, one output
        BiFunction<Double, Integer, String> movieScore = (rating, year) -> {
            double ageBonus = (2024 - year) * 0.01; // Bonus for classics
            double totalScore = rating + ageBonus;
            return String.format("%.2f", totalScore);
        };
        
        System.out.println("\\nMovies with age-adjusted scores:");
        movies.forEach(movie -> {
            String score = movieScore.apply(movie.getRating(), movie.getYear());
            System.out.println("  " + movie.getTitle() + ": " + score);
        });
    }
    
    // šŸŽÆ Advanced Lambda Patterns
    public void demonstrateAdvancedPatterns() {
        System.out.println("\\nšŸš€ Advanced Lambda Patterns");
        System.out.println("===========================");
        
        // Higher-order functions - functions that return functions
        Function<String, Predicate<Movie>> genreFilter = genre -> 
            movie -> genre.equals(movie.getGenre());
        
        Function<Double, Predicate<Movie>> ratingFilter = minRating -> 
            movie -> movie.getRating() >= minRating;
        
        // Use the factory functions
        Predicate<Movie> sciFiFilter = genreFilter.apply("Sci-Fi");
        Predicate<Movie> highRatingFilter = ratingFilter.apply(8.5);
        
        System.out.println("High-rated Sci-Fi movies:");
        [movies.stream](<http://movies.stream>)()
              .filter(sciFiFilter.and(highRatingFilter))
              .forEach(movie -> System.out.println("  šŸš€ " + movie));
        
        // Currying - transforming multi-argument function into chain of single-argument functions
        Function<String, Function<Integer, Function<Double, Movie>>> movieCreator = 
            title -> year -> rating -> 
                new Movie(title, "Unknown", year, rating, 0, Arrays.asList());
        
        Movie newMovie = movieCreator
            .apply("Test Movie")
            .apply(2024)
            .apply(7.5);
        
        System.out.println("\\nCurried movie creation: " + newMovie);
        
        // Lambda with exception handling
        Function<String, Optional<Integer>> safeParser = input -> {
            try {
                return Optional.of(Integer.parseInt(input));
            } catch (NumberFormatException e) {
                return Optional.empty();
            }
        };
        
        List<String> inputs = Arrays.asList("2010", "invalid", "1999", "not-a-year");
        System.out.println("\\nSafe parsing results:");
        [inputs.stream](<http://inputs.stream>)()
              .map(safeParser)
              .forEach(result -> 
                  System.out.println("  " + [result.map](<http://result.map>)(String::valueOf).orElse("Invalid")));
    }
}