Lesson Completion
Back to course

Lambda Expressions: Anonymous Functions in Java

Beginner
12 minutes4.6Java

1. The Hook (The "Byte-Sized" Intro)

  • In a Nutshell: Lambda expressions are anonymous functions—methods without names.
  • Syntax: (parameters) -> expression or (parameters) -> { statements }. Replace verbose anonymous classes. Require functional interface (single abstract method).
  • Variable capture: Can access effectively final variables from enclosing scope.
  • Benefits: 70% less code, readable, enables functional programming.
  • Use for: Comparators, event handlers, callbacks, Stream API.
  • Type inference: Compiler infers parameter types!

Think of vending machine. Anonymous class = build custom machine for each snack (overkill!). Lambda = programmable machine: "dispenseCoke→press button 1", "dispenseWater→press button 2". One machine, configurable behavior!


2. Conceptual Clarity (The "Simple" Tier)

💡 The Analogy: Remote Control

  • Before (Anonymous Class): Build entire custom remote for each TV function
  • After (Lambda): Universal remote with programmable buttons
  • Lambda: The programming ("button1 → volumeUp")

Lambda Evolution

graph LR A[Anonymous Class<br/>10 lines] -->|Java 8| B[Lambda Expression<br/>1 line] B -->|Even shorter| C[Method Reference<br/>Class::method]

3. Technical Mastery (The "Deep Dive")

Lambda Syntax Variations

java
// No parameters () -> System.out.println("Hello") // One parameter (parentheses optional) x -> x * 2 (x) -> x * 2 // Same // Multiple parameters (x, y) -> x + y // With block (x, y) -> { int sum = x + y; return sum; } // With type declarations (usually inferred) (int x, int y) -> x + y

4. Interactive & Applied Code

java
import java.util.*; import java.util.function.*; public class LambdaExpressionsDemo { public static void main(String[] args) { demonstrateBasicLambdas(); demonstrateVsAnonymousClass(); demonstrateVariableCapture(); demonstrateCommonUseCases(); } // Basic lambda syntax static void demonstrateBasicLambdas() { System.out.println("=== BASIC LAMBDAS ==="); // Runnable (no parameters) Runnable r1 = () -> System.out.println("Running!"); r1.run(); // Predicate (one parameter) Predicate<String> isEmpty = s -> s.isEmpty(); System.out.println("Is empty: " + isEmpty.test("")); // Comparator (two parameters) Comparator<Integer> comp = (a, b) -> a - b; System.out.println("Compare 5 vs 3: " + comp.compare(5, 3)); // With block Function<Integer, Integer> square = (x) -> { int result = x * x; System.out.println("Squaring " + x); return result; }; System.out.println("Square of 5: " + square.apply(5)); } // Lambda vs Anonymous Class static void demonstrateVsAnonymousClass() { System.out.println("\n=== LAMBDA VS ANONYMOUS CLASS ==="); // ❌ VERBOSE: Anonymous class (Java 7 and before) Runnable oldWay = new Runnable() { @Override public void run() { System.out.println("Old way: 5 lines of boilerplate!"); } }; // ✅ CONCISE: Lambda expression (Java 8+) Runnable newWay = () -> System.out.println("New way: 1 line!"); oldWay.run(); newWay.run(); // Comparator example List<String> names = Arrays.asList("Charlie", "Alice", "Bob"); // Old way Collections.sort(names, new Comparator<String>() { @Override public int compare(String a, String b) { return a.compareTo(b); } }); // New way names.sort((a, b) -> a.compareTo(b)); System.out.println("Sorted: " + names); } // Variable capture (effectively final) static void demonstrateVariableCapture() { System.out.println("\n=== VARIABLE CAPTURE ==="); String prefix = "Hello, "; // Effectively final int multiplier = 2; // Effectively final // Lambda captures variables from enclosing scope Consumer<String> greeter = name -> System.out.println(prefix + name); Function<Integer, Integer> doubler = x -> x * multiplier; greeter.accept("Alice"); System.out.println("Double 5: " + doubler.apply(5)); // ❌ COMPILE ERROR: Can't modify captured variables // prefix = "Hi, "; // Error: variable not effectively final! // ✅ OK: Local variables inside lambda Function<Integer, Integer> adder = x -> { int localVar = 10; // OK to declare return x + localVar; }; } // Common use cases static void demonstrateCommonUseCases() { System.out.println("\n=== COMMON USE CASES ==="); List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5); // 1. forEach (Consumer) numbers.forEach(n -> System.out.print(n + " ")); System.out.println(); // 2. Filtering (Predicate) List<Integer> evens = new ArrayList<>(); numbers.forEach(n -> { if (n % 2 == 0) { evens.add(n); } }); System.out.println("Evens: " + evens); // 3. Sorting (Comparator) List<String> words = Arrays.asList("apple", "pie", "a", "banana"); words.sort((a, b) -> Integer.compare(a.length(), b.length())); System.out.println("By length: " + words); // 4. Thread creation (Runnable) Thread thread = new Thread(() -> { System.out.println("Running in thread: " + Thread.currentThread().getName()); }); thread.start(); } } // Custom functional interface @FunctionalInterface interface Calculator { int calculate(int a, int b); } class CustomFunctionalInterfaceDemo { public static void main(String[] args) { // Lambda implements custom interface Calculator add = (a, b) -> a + b; Calculator multiply = (a, b) -> a * b; Calculator max = (a, b) -> a > b ? a : b; System.out.println("5 + 3 = " + add.calculate(5, 3)); System.out.println("5 * 3 = " + multiply.calculate(5, 3)); System.out.println("max(5, 3) = " + max.calculate(5, 3)); } }

5. The Comparison & Decision Layer

FeatureAnonymous ClassLambda
Lines of code5-101
this keywordRefers to instanceRefers to enclosing class
Can implementAny interface/classOnly functional interfaces
SerializableIf class isOnly if target type is
ShadowingCan shadow variablesCannot shadow

6. The "Interview Corner" (The Edge)

The "Killer" Interview Question: "What does 'effectively final' mean for lambda variable capture?" Answer: Variable doesn't need final keyword, but can't be modified after initialization!

java
int x = 10; // Not declared final // Lambda captures x Runnable r = () -> System.out.println(x); // ❌ COMPILE ERROR: Can't modify captured variable // x = 20; // Error: variable x must be effectively final // Why? Lambda may execute later (different context). // If x changed, which value should lambda see?

Workaround:

java
final int[] x = {10}; // Array is final, contents mutable Runnable r = () -> System.out.println(x[0]); x[0] = 20; // ✅ OK: Modifying array contents

Pro-Tips:

  1. Type inference:
java
// ❌ Verbose: Explicit types Comparator<String> c = (String a, String b) -> a.compareTo(b); // ✅ Concise: Inferred types Comparator<String> c = (a, b) -> a.compareTo(b);
  1. Return statement optional for single expression:
java
// ❌ Unnecessary return Function<Integer, Integer> f = x -> { return x * 2; }; // ✅ Implicit return Function<Integer, Integer> f = x -> x * 2;
  1. Parentheses optional for single parameter:
java
Consumer<String> c1 = (s) -> System.out.println(s); Consumer<String> c2 = s -> System.out.println(s); // ✅ Cleaner

Topics Covered

Java Fundamentals

Tags

#java#programming#beginner-friendly

Last Updated

2025-02-01