Lambda Expressions Syntax in Java

Lambda expressions provide a clear and concise way to represent instances of single-method interfaces (functional interfaces) using expressions.

1. Basic Lambda Syntax

Basic Syntax Structure

import java.util.*;
public class LambdaBasicSyntax {
// Functional interfaces for demonstration
@FunctionalInterface
interface SimpleFunction {
void execute();
}
@FunctionalInterface
interface StringProcessor {
String process(String input);
}
@FunctionalInterface
interface MathOperation {
int operate(int a, int b);
}
@FunctionalInterface
interface Validator {
boolean isValid(String value);
}
public static void main(String[] args) {
System.out.println("=== Basic Lambda Syntax ===");
// 1. No parameters, no return value
SimpleFunction greet = () -> System.out.println("Hello, World!");
greet.execute();
// 2. Single parameter, no type declaration
StringProcessor toUpper = s -> s.toUpperCase();
System.out.println("Uppercase: " + toUpper.process("hello"));
// 3. Single parameter with parentheses (optional for single parameter)
StringProcessor toLower = (s) -> s.toLowerCase();
System.out.println("Lowercase: " + toLower.process("HELLO"));
// 4. Multiple parameters
MathOperation addition = (a, b) -> a + b;
System.out.println("5 + 3 = " + addition.operate(5, 3));
// 5. Multiple parameters with explicit types
MathOperation subtraction = (int a, int b) -> a - b;
System.out.println("5 - 3 = " + subtraction.operate(5, 3));
// 6. With return statement and curly braces
MathOperation multiplication = (a, b) -> {
int result = a * b;
return result;
};
System.out.println("5 * 3 = " + multiplication.operate(5, 3));
// 7. Single expression without return statement
MathOperation division = (a, b) -> a / b;
System.out.println("6 / 3 = " + division.operate(6, 3));
demonstrateMethodReferences();
}
public static void demonstrateMethodReferences() {
System.out.println("\n=== Method References vs Lambdas ===");
List<String> names = Arrays.asList("John", "Alice", "Bob", "Carol");
// Lambda expression
names.forEach(name -> System.out.println(name));
// Method reference (equivalent)
names.forEach(System.out::println);
// Lambda for string transformation
List<String> upperNames = new ArrayList<>();
names.forEach(name -> upperNames.add(name.toUpperCase()));
// Method reference equivalent
List<String> lowerNames = new ArrayList<>();
names.forEach(lowerNames::add); // Instance method reference
System.out.println("Uppercase: " + upperNames);
System.out.println("Lowercase: " + lowerNames);
}
}

Lambda vs Anonymous Class

import java.util.*;
public class LambdaVsAnonymous {
@FunctionalInterface
interface Calculator {
int calculate(int x, int y);
}
@FunctionalInterface
interface Greeter {
String greet(String name);
}
@FunctionalInterface
interface Condition {
boolean test(int number);
}
public static void main(String[] args) {
System.out.println("=== Lambda vs Anonymous Class ===");
// Anonymous class implementation
Calculator anonymousAdd = new Calculator() {
@Override
public int calculate(int x, int y) {
return x + y;
}
};
// Lambda equivalent
Calculator lambdaAdd = (x, y) -> x + y;
System.out.println("Anonymous: 5 + 3 = " + anonymousAdd.calculate(5, 3));
System.out.println("Lambda: 5 + 3 = " + lambdaAdd.calculate(5, 3));
// More complex example
Greeter anonymousGreet = new Greeter() {
@Override
public String greet(String name) {
return "Hello, " + name + "!";
}
};
Greeter lambdaGreet = name -> "Hello, " + name + "!";
System.out.println("Anonymous: " + anonymousGreet.greet("John"));
System.out.println("Lambda: " + lambdaGreet.greet("John"));
// With multiple statements
Condition anonymousCondition = new Condition() {
@Override
public boolean test(int number) {
boolean isEven = number % 2 == 0;
boolean isPositive = number > 0;
return isEven && isPositive;
}
};
Condition lambdaCondition = number -> {
boolean isEven = number % 2 == 0;
boolean isPositive = number > 0;
return isEven && isPositive;
};
System.out.println("Anonymous test(4): " + anonymousCondition.test(4));
System.out.println("Lambda test(4): " + lambdaCondition.test(4));
demonstrateRunnableExample();
}
public static void demonstrateRunnableExample() {
System.out.println("\n=== Runnable Example ===");
// Anonymous class
Runnable anonymousRunnable = new Runnable() {
@Override
public void run() {
System.out.println("Running from anonymous class");
System.out.println("Thread: " + Thread.currentThread().getName());
}
};
// Lambda equivalent
Runnable lambdaRunnable = () -> {
System.out.println("Running from lambda");
System.out.println("Thread: " + Thread.currentThread().getName());
};
// Execute both
new Thread(anonymousRunnable).start();
new Thread(lambdaRunnable).start();
// Even shorter lambda
new Thread(() -> System.out.println("Ultra short lambda")).start();
}
}

2. Lambda Parameters and Return Types

Various Parameter Scenarios

import java.util.*;
import java.util.function.*;
public class LambdaParameters {
public static void main(String[] args) {
System.out.println("=== Lambda Parameters and Return Types ===");
// 1. No parameters
Supplier<String> messageSupplier = () -> "Hello from supplier!";
System.out.println(messageSupplier.get());
Runnable noParamRunnable = () -> System.out.println("No parameters");
noParamRunnable.run();
// 2. Single parameter - type inference
Function<String, Integer> stringLength = s -> s.length();
System.out.println("Length of 'Hello': " + stringLength.apply("Hello"));
Consumer<String> printer = s -> System.out.println("Value: " + s);
printer.accept("Test");
// 3. Single parameter with explicit type
Function<String, String> explicitType = (String s) -> s.toUpperCase();
System.out.println("Explicit type: " + explicitType.apply("hello"));
// 4. Multiple parameters
BiFunction<Integer, Integer, Integer> adder = (a, b) -> a + b;
System.out.println("5 + 3 = " + adder.apply(5, 3));
// 5. Multiple parameters with explicit types
BiFunction<String, String, String> concatenator = 
(String s1, String s2) -> s1 + " " + s2;
System.out.println("Concatenated: " + concatenator.apply("Hello", "World"));
// 6. No parameters with block body
Supplier<Double> randomSupplier = () -> {
Random random = new Random();
return random.nextDouble();
};
System.out.println("Random number: " + randomSupplier.get());
// 7. Complex multi-parameter lambda
TriFunction<Integer, Integer, Integer, Integer> complexOperation = 
(a, b, c) -> {
int sum = a + b + c;
int product = a * b * c;
return sum + product;
};
System.out.println("Complex operation: " + complexOperation.apply(1, 2, 3));
demonstrateReturnTypes();
}
// Custom functional interface for three parameters
@FunctionalInterface
interface TriFunction<T, U, V, R> {
R apply(T t, U u, V v);
}
public static void demonstrateReturnTypes() {
System.out.println("\n=== Return Type Examples ===");
// Implicit return (single expression)
Function<Integer, Integer> square = x -> x * x;
System.out.println("Square of 5: " + square.apply(5));
// Explicit return with block
Function<Integer, String> numberInfo = x -> {
if (x % 2 == 0) {
return x + " is even";
} else {
return x + " is odd";
}
};
System.out.println(numberInfo.apply(4));
System.out.println(numberInfo.apply(7));
// Returning collections
Function<Integer, List<Integer>> numberList = count -> {
List<Integer> list = new ArrayList<>();
for (int i = 1; i <= count; i++) {
list.add(i);
}
return list;
};
System.out.println("Number list: " + numberList.apply(5));
// Void return type
Consumer<List<String>> listProcessor = list -> {
for (String item : list) {
System.out.println("Processing: " + item.toUpperCase());
}
};
listProcessor.accept(Arrays.asList("apple", "banana", "cherry"));
}
}

3. Built-in Functional Interfaces with Lambdas

java.util.function Package Examples

import java.util.*;
import java.util.function.*;
import java.util.stream.Collectors;
public class BuiltInFunctionalInterfaces {
public static void main(String[] args) {
System.out.println("=== Built-in Functional Interfaces ===");
List<String> names = Arrays.asList("John", "Alice", "Bob", "Carol", "David", "Eve");
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// 1. Predicate - boolean valued function
System.out.println("\n=== Predicate Examples ===");
Predicate<String> startsWithA = s -> s.startsWith("A");
Predicate<String> lengthGreaterThan3 = s -> s.length() > 3;
List<String> filteredNames = names.stream()
.filter(startsWithA.or(lengthGreaterThan3))
.collect(Collectors.toList());
System.out.println("Filtered names: " + filteredNames);
// Predicate composition
Predicate<Integer> isEven = n -> n % 2 == 0;
Predicate<Integer> isGreaterThan5 = n -> n > 5;
List<Integer> evenAndGreaterThan5 = numbers.stream()
.filter(isEven.and(isGreaterThan5))
.collect(Collectors.toList());
System.out.println("Even and > 5: " + evenAndGreaterThan5);
// 2. Function - transforms input to output
System.out.println("\n=== Function Examples ===");
Function<String, Integer> stringLength = String::length;
Function<String, String> toUpperCase = String::toUpperCase;
Function<String, String> addPrefix = s -> "Mr. " + s;
List<Integer> nameLengths = names.stream()
.map(stringLength)
.collect(Collectors.toList());
System.out.println("Name lengths: " + nameLengths);
// Function chaining
Function<String, String> nameTransformer = toUpperCase.andThen(addPrefix);
List<String> transformedNames = names.stream()
.map(nameTransformer)
.collect(Collectors.toList());
System.out.println("Transformed names: " + transformedNames);
// 3. Consumer - accepts input, returns void
System.out.println("\n=== Consumer Examples ===");
Consumer<String> simplePrinter = System.out::println;
Consumer<String> detailedPrinter = s -> System.out.println("Name: " + s + ", Length: " + s.length());
System.out.println("Simple printing:");
names.forEach(simplePrinter);
System.out.println("\nDetailed printing:");
names.forEach(detailedPrinter);
// Consumer chaining
Consumer<String> chainedConsumer = simplePrinter.andThen(detailedPrinter);
System.out.println("\nChained consumer:");
chainedConsumer.accept("Test");
// 4. Supplier - provides results
System.out.println("\n=== Supplier Examples ===");
Supplier<Double> randomSupplier = Math::random;
Supplier<List<String>> listSupplier = ArrayList::new;
Supplier<String> constantSupplier = () -> "Constant Value";
System.out.println("Random: " + randomSupplier.get());
System.out.println("Constant: " + constantSupplier.get());
System.out.println("New list: " + listSupplier.get());
// 5. UnaryOperator and BinaryOperator
System.out.println("\n=== Operator Examples ===");
UnaryOperator<String> stringDoubler = s -> s + s;
BinaryOperator<Integer> multiplier = (a, b) -> a * b;
System.out.println("Double 'Hi': " + stringDoubler.apply("Hi"));
System.out.println("5 * 3 = " + multiplier.apply(5, 3));
demonstrateMoreFunctionalInterfaces();
}
public static void demonstrateMoreFunctionalInterfaces() {
System.out.println("\n=== More Functional Interfaces ===");
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// BiFunction - two inputs, one output
BiFunction<Integer, Integer, String> sumFormatter = 
(a, b) -> String.format("%d + %d = %d", a, b, a + b);
System.out.println("Sum format: " + sumFormatter.apply(5, 3));
// BiConsumer - two inputs, no output
BiConsumer<String, Integer> nameAgePrinter = 
(name, age) -> System.out.println(name + " is " + age + " years old");
nameAgePrinter.accept("John", 25);
// BiPredicate - two inputs, boolean output
BiPredicate<String, String> sameLength = 
(s1, s2) -> s1.length() == s2.length();
System.out.println("Same length? " + sameLength.test("hello", "world"));
// Specialized functional interfaces
IntFunction<String> intToString = i -> "Number: " + i;
ToIntFunction<String> stringToLength = String::length;
IntUnaryOperator square = x -> x * x;
System.out.println(intToString.apply(42));
System.out.println("Length of 'Hello': " + stringToLength.applyAsInt("Hello"));
System.out.println("Square of 9: " + square.applyAsInt(9));
}
}

4. Lambda Expressions with Collections

Using Lambdas with Java Collections

import java.util.*;
import java.util.stream.Collectors;
public class LambdaWithCollections {
static class Person {
String name;
int age;
String city;
public Person(String name, int age, String city) {
this.name = name;
this.age = age;
this.city = city;
}
public String getName() { return name; }
public int getAge() { return age; }
public String getCity() { return city; }
@Override
public String toString() {
return String.format("Person{name='%s', age=%d, city='%s'}", name, age, city);
}
}
public static void main(String[] args) {
System.out.println("=== Lambda Expressions with Collections ===");
List<Person> people = Arrays.asList(
new Person("John", 25, "New York"),
new Person("Alice", 30, "Boston"),
new Person("Bob", 22, "Chicago"),
new Person("Carol", 35, "New York"),
new Person("David", 28, "Boston"),
new Person("Eve", 19, "Chicago")
);
// 1. Sorting with Comparator lambda
System.out.println("\n=== Sorting ===");
// Sort by age (ascending)
people.sort((p1, p2) -> Integer.compare(p1.getAge(), p2.getAge()));
System.out.println("Sorted by age:");
people.forEach(System.out::println);
// Sort by name (descending)
people.sort((p1, p2) -> p2.getName().compareTo(p1.getName()));
System.out.println("\nSorted by name (descending):");
people.forEach(System.out::println);
// Using Comparator.comparing with method reference
people.sort(Comparator.comparing(Person::getCity).thenComparing(Person::getAge));
System.out.println("\nSorted by city then age:");
people.forEach(System.out::println);
// 2. Filtering with Predicate
System.out.println("\n=== Filtering ===");
List<Person> adults = people.stream()
.filter(p -> p.getAge() >= 25)
.collect(Collectors.toList());
System.out.println("Adults (25+): " + adults);
List<Person> newYorkers = people.stream()
.filter(p -> "New York".equals(p.getCity()))
.collect(Collectors.toList());
System.out.println("New York residents: " + newYorkers);
// 3. Mapping with Function
System.out.println("\n=== Mapping ===");
List<String> names = people.stream()
.map(Person::getName)
.collect(Collectors.toList());
System.out.println("Names: " + names);
List<String> nameAgeCombinations = people.stream()
.map(p -> p.getName() + " (" + p.getAge() + ")")
.collect(Collectors.toList());
System.out.println("Name with age: " + nameAgeCombinations);
// 4. Grouping with Collectors
System.out.println("\n=== Grouping ===");
Map<String, List<Person>> peopleByCity = people.stream()
.collect(Collectors.groupingBy(Person::getCity));
System.out.println("People by city:");
peopleByCity.forEach((city, cityPeople) -> {
System.out.println(city + ": " + cityPeople);
});
Map<String, Long> countByCity = people.stream()
.collect(Collectors.groupingBy(Person::getCity, Collectors.counting()));
System.out.println("Count by city: " + countByCity);
demonstrateMapOperations();
}
public static void demonstrateMapOperations() {
System.out.println("\n=== Map Operations with Lambdas ===");
Map<String, Integer> wordCounts = new HashMap<>();
wordCounts.put("apple", 3);
wordCounts.put("banana", 5);
wordCounts.put("cherry", 2);
// forEach with BiConsumer
System.out.println("Word counts:");
wordCounts.forEach((word, count) -> 
System.out.println(word + ": " + count));
// computeIfAbsent
wordCounts.computeIfAbsent("date", k -> 1);
wordCounts.computeIfAbsent("apple", k -> 10); // Won't compute, already exists
System.out.println("After computeIfAbsent: " + wordCounts);
// computeIfPresent
wordCounts.computeIfPresent("banana", (word, count) -> count + 10);
wordCounts.computeIfPresent("grape", (word, count) -> count + 1); // Won't compute, doesn't exist
System.out.println("After computeIfPresent: " + wordCounts);
// merge
wordCounts.merge("apple", 5, (oldValue, newValue) -> oldValue + newValue);
wordCounts.merge("fig", 1, (oldValue, newValue) -> oldValue + newValue);
System.out.println("After merge: " + wordCounts);
// replaceAll
wordCounts.replaceAll((word, count) -> count * 2);
System.out.println("After replaceAll (doubled): " + wordCounts);
}
}

5. Variable Capture in Lambdas

Effectively Final and Variable Capture

import java.util.*;
import java.util.function.*;
public class LambdaVariableCapture {
private String instanceVariable = "Instance Variable";
private static String staticVariable = "Static Variable";
public static void main(String[] args) {
System.out.println("=== Variable Capture in Lambdas ===");
LambdaVariableCapture example = new LambdaVariableCapture();
example.demonstrateLocalVariableCapture();
example.demonstrateInstanceAndStaticCapture();
example.demonstrateEffectivelyFinal();
}
public void demonstrateLocalVariableCapture() {
System.out.println("\n=== Local Variable Capture ===");
// Effectively final local variable
final String finalLocal = "Final Local";
String effectivelyFinal = "Effectively Final";
// Lambda capturing local variables
Supplier<String> localCapture = () -> {
// Can read both final and effectively final variables
return finalLocal + " and " + effectivelyFinal;
};
System.out.println("Local capture: " + localCapture.get());
// Trying to modify captured variable (will cause compile error)
// effectivelyFinal = "Modified"; // Uncommenting this will break the lambda
// Array capture (array reference is effectively final, but contents can be modified)
int[] counter = {0};
IntSupplier counterLambda = () -> ++counter[0]; // Modifying array content is allowed
System.out.println("Counter: " + counterLambda.getAsInt());
System.out.println("Counter: " + counterLambda.getAsInt());
System.out.println("Counter: " + counterLambda.getAsInt());
}
public void demonstrateInstanceAndStaticCapture() {
System.out.println("\n=== Instance and Static Variable Capture ===");
// Lambda capturing instance variable
Supplier<String> instanceCapture = () -> this.instanceVariable;
System.out.println("Instance capture: " + instanceCapture.get());
// Lambda capturing static variable
Supplier<String> staticCapture = () -> staticVariable;
System.out.println("Static capture: " + staticCapture.get());
// Modifying instance variables from lambda
Runnable instanceModifier = () -> {
this.instanceVariable = "Modified from lambda";
staticVariable = "Static modified from lambda";
};
instanceModifier.run();
System.out.println("After modification:");
System.out.println("Instance: " + instanceCapture.get());
System.out.println("Static: " + staticCapture.get());
}
public void demonstrateEffectivelyFinal() {
System.out.println("\n=== Effectively Final Concept ===");
List<String> names = Arrays.asList("John", "Alice", "Bob");
// This works - list reference is effectively final
names.forEach(name -> System.out.println(name));
// This would cause compile error - modifying effectively final variable
// String prefix = "Mr. ";
// names.forEach(name -> {
//     prefix = "Dr. "; // Compile error - cannot assign to effectively final variable
//     System.out.println(prefix + name);
// });
// Workaround using array or wrapper
String[] prefixArray = {"Mr. "};
names.forEach(name -> {
System.out.println(prefixArray[0] + name);
// Can modify array content, but not the array reference
prefixArray[0] = "Dr. "; // This is allowed!
});
// Using AtomicReference as wrapper
java.util.concurrent.atomic.AtomicReference<String> prefixRef = 
new java.util.concurrent.atomic.AtomicReference<>("Hello ");
names.forEach(name -> {
String currentPrefix = prefixRef.get();
System.out.println(currentPrefix + name);
prefixRef.set("Hi "); // This is allowed!
});
}
public void demonstrateThisInLambda() {
System.out.println("\n=== 'this' in Lambda ===");
// In lambda, 'this' refers to the enclosing instance
Runnable lambdaWithThis = () -> {
System.out.println("Lambda this: " + this);
System.out.println("Lambda this class: " + this.getClass().getSimpleName());
};
lambdaWithThis.run();
// Compare with anonymous class
Runnable anonymousWithThis = new Runnable() {
@Override
public void run() {
System.out.println("Anonymous this: " + this);
System.out.println("Anonymous this class: " + this.getClass().getSimpleName());
}
};
anonymousWithThis.run();
}
}

6. Real-World Lambda Examples

Practical Lambda Usage Scenarios

import java.util.*;
import java.util.concurrent.*;
import java.util.function.*;
import java.util.stream.*;
public class RealWorldLambdaExamples {
static class Product {
String name;
double price;
String category;
int stock;
public Product(String name, double price, String category, int stock) {
this.name = name;
this.price = price;
this.category = category;
this.stock = stock;
}
public String getName() { return name; }
public double getPrice() { return price; }
public String getCategory() { return category; }
public int getStock() { return stock; }
@Override
public String toString() {
return String.format("Product{name='%-12s', price=$%-6.2f, category='%-10s', stock=%d}", 
name, price, category, stock);
}
}
public static void main(String[] args) throws Exception {
System.out.println("=== Real-World Lambda Examples ===");
List<Product> products = Arrays.asList(
new Product("Laptop", 999.99, "Electronics", 10),
new Product("Smartphone", 699.99, "Electronics", 25),
new Product("Headphones", 149.99, "Electronics", 50),
new Product("T-Shirt", 19.99, "Clothing", 100),
new Product("Jeans", 49.99, "Clothing", 75),
new Product("Book", 12.99, "Education", 200),
new Product("Desk Lamp", 29.99, "Home", 30)
);
// 1. E-commerce product filtering and transformation
demonstrateECommerceOperations(products);
// 2. Data processing pipeline
demonstrateDataProcessing(products);
// 3. Asynchronous operations with CompletableFuture
demonstrateAsyncOperations();
// 4. Event handling and callbacks
demonstrateEventHandling();
}
public static void demonstrateECommerceOperations(List<Product> products) {
System.out.println("\n=== E-Commerce Operations ===");
// Filter electronics products
List<Product> electronics = products.stream()
.filter(p -> "Electronics".equals(p.getCategory()))
.collect(Collectors.toList());
System.out.println("Electronics products:");
electronics.forEach(System.out::println);
// Apply discount to clothing products
Function<Product, Product> applyClothingDiscount = p -> {
if ("Clothing".equals(p.getCategory())) {
return new Product(p.getName(), p.getPrice() * 0.8, p.getCategory(), p.getStock());
}
return p;
};
List<Product> discountedProducts = products.stream()
.map(applyClothingDiscount)
.collect(Collectors.toList());
System.out.println("\nProducts with clothing discount:");
discountedProducts.forEach(System.out::println);
// Group products by category
Map<String, List<Product>> productsByCategory = products.stream()
.collect(Collectors.groupingBy(Product::getCategory));
System.out.println("\nProducts by category:");
productsByCategory.forEach((category, productList) -> {
System.out.println(category + ": " + productList.size() + " products");
});
// Calculate total inventory value
double totalValue = products.stream()
.mapToDouble(p -> p.getPrice() * p.getStock())
.sum();
System.out.printf("\nTotal inventory value: $%.2f%n", totalValue);
}
public static void demonstrateDataProcessing(List<Product> products) {
System.out.println("\n=== Data Processing Pipeline ===");
// Complex data processing pipeline
Map<String, Double> categoryAveragePrice = products.stream()
.collect(Collectors.groupingBy(
Product::getCategory,
Collectors.averagingDouble(Product::getPrice)
));
System.out.println("Average price by category:");
categoryAveragePrice.forEach((category, avgPrice) -> 
System.out.printf("%-12s: $%.2f%n", category, avgPrice));
// Products that need restocking (stock < 20)
Predicate<Product> needsRestocking = p -> p.getStock() < 20;
List<String> productsToRestock = products.stream()
.filter(needsRestocking)
.map(Product::getName)
.collect(Collectors.toList());
System.out.println("\nProducts needing restock: " + productsToRestock);
// Price range analysis
DoubleSummaryStatistics priceStats = products.stream()
.mapToDouble(Product::getPrice)
.summaryStatistics();
System.out.println("\nPrice statistics:");
System.out.printf("Count: %d, Min: $%.2f, Max: $%.2f, Average: $%.2f%n",
priceStats.getCount(), priceStats.getMin(), 
priceStats.getMax(), priceStats.getAverage());
}
public static void demonstrateAsyncOperations() throws Exception {
System.out.println("\n=== Asynchronous Operations ===");
// Simulate async tasks with CompletableFuture
CompletableFuture<String> asyncTask1 = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(1000);
return "Result from async task 1";
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
});
CompletableFuture<String> asyncTask2 = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(500);
return "Result from async task 2";
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
});
// Combine results when both complete
CompletableFuture<String> combined = asyncTask1.thenCombine(asyncTask2, 
(result1, result2) -> result1 + " + " + result2);
System.out.println("Combined result: " + combined.get());
// Chain async operations
CompletableFuture<Integer> chainedOperation = CompletableFuture.supplyAsync(() -> 5)
.thenApplyAsync(x -> x * 2)
.thenApplyAsync(x -> x + 10)
.thenApplyAsync(x -> x * x);
System.out.println("Chained operation result: " + chainedOperation.get());
}
public static void demonstrateEventHandling() {
System.out.println("\n=== Event Handling ===");
// Simple event system
class EventManager {
private Map<String, List<Consumer<String>>> listeners = new HashMap<>();
public void addListener(String eventType, Consumer<String> listener) {
listeners.computeIfAbsent(eventType, k -> new ArrayList<>()).add(listener);
}
public void fireEvent(String eventType, String data) {
List<Consumer<String>> eventListeners = listeners.get(eventType);
if (eventListeners != null) {
eventListeners.forEach(listener -> listener.accept(data));
}
}
}
EventManager eventManager = new EventManager();
// Register lambda event handlers
eventManager.addListener("login", username -> 
System.out.println("User logged in: " + username));
eventManager.addListener("purchase", product -> 
System.out.println("Purchase made: " + product));
eventManager.addListener("error", errorMessage -> 
System.err.println("Error occurred: " + errorMessage));
// Fire events
eventManager.fireEvent("login", "john_doe");
eventManager.fireEvent("purchase", "Laptop");
eventManager.fireEvent("error", "Connection timeout");
// Button click simulation (like in GUI programming)
class Button {
private String text;
private Runnable onClick;
public Button(String text) {
this.text = text;
}
public void setOnClick(Runnable onClick) {
this.onClick = onClick;
}
public void click() {
System.out.println("Button '" + text + "' clicked");
if (onClick != null) {
onClick.run();
}
}
}
Button saveButton = new Button("Save");
saveButton.setOnClick(() -> {
System.out.println("Saving data...");
System.out.println("Data saved successfully!");
});
Button cancelButton = new Button("Cancel");
cancelButton.setOnClick(() -> System.out.println("Operation cancelled"));
// Simulate button clicks
saveButton.click();
cancelButton.click();
}
}

Summary

Key Lambda Syntax Rules:

  1. Basic Syntax: (parameters) -> expression
  2. Parameter Rules:
  • No parameters: () -> expression
  • Single parameter: param -> expression or (param) -> expression
  • Multiple parameters: (param1, param2) -> expression
  1. Body Rules:
  • Single expression: implicit return
  • Block body: { statements; return value; }

Important Concepts:

  1. Functional Interfaces: Lambdas work with interfaces having exactly one abstract method
  2. Type Inference: Compiler can often infer parameter types
  3. Variable Capture: Lambdas can capture effectively final variables
  4. Method References: Shorthand for common lambda patterns

Common Use Cases:

  1. Collection operations: filtering, mapping, sorting
  2. Event handling: GUI events, callbacks
  3. Asynchronous programming: CompletableFuture, threads
  4. Stream processing: data transformation pipelines
  5. Functional programming: higher-order functions

Best Practices:

  1. Keep lambdas short and focused
  2. Use method references when possible
  3. Avoid complex logic in lambdas
  4. Be mindful of variable capture
  5. Use meaningful parameter names

Lambda expressions make Java code more concise, readable, and functional, enabling modern programming paradigms while maintaining type safety.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper