Skip to content
IRC-CodingIRC-Coding
Functional ProgrammingLambdaFunctional InterfacesHigher-Order FunctionsJava 8Stream APIPure FunctionsImmutabilityProgramming LanguageProgramming LanguagesProgramming

Functional Programming: Lambda, Interfaces & Higher-Order Functions

Master functional programming with lambda expressions, functional interfaces, and higher-order functions. Java 8+ and Python examples.

S

schutzgeist

17 min read
Functional Programming: Lambda, Interfaces & Higher-Order Functions

Functional Programming: Lambdas, Functional Interfaces & Higher-Order Functions

Functional programming is a programming paradigm that treats functions as first-class citizens. It emphasizes immutable data and pure functions, leading to better testability and easier parallelization.

Functional Programming Fundamentals

Core Principles

  • Pure Functions: Functions without side effects
  • Immutability: Unchangeable data structures
  • First-Class Functions: Functions as values
  • Higher-Order Functions: Functions that accept other functions as parameters
  • Function Composition: Combining functions together

Pure vs Impure Functions

public class FunctionExamples {
    
    // Pure function - no side effects
    public static int add(int a, int b) {
        return a + b;
        // Always returns the same result for the same inputs
        // No side effects
    }
    
    // Impure function - with side effects
    private static int counter = 0;
    
    public static int addToCounter(int value) {
        counter += value; // Side effect: modifies global state
        return counter;
        // Different results for the same inputs
    }
    
    // Pure function for string processing
    public static String capitalize(String input) {
        if (input == null) return null;
        return input.toUpperCase();
    }
    
    // Impure function with external dependency
    public static String getCurrentTimeFormatted() {
        return DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(LocalDateTime.now());
        // Depends on the current time
    }
}

Lambda Expressions in Java

Syntax and Basics

public class LambdaBasics {
    
    // Traditional anonymous class
    public static void traditionalApproach() {
        Runnable runnable1 = new Runnable() {
            @Override
            public void run() {
                System.out.println("Traditional approach");
            }
        };
        runnable1.run();
    }
    
    // Lambda expression
    public static void lambdaApproach() {
        Runnable runnable2 = () -> System.out.println("Lambda approach");
        runnable2.run();
    }
    
    // Lambda with various syntax variants
    public static void lambdaSyntaxExamples() {
        
        // No parameters
        Runnable noParams = () -> System.out.println("No parameters");
        
        // Single parameter
        Consumer<String> oneParam = s -> System.out.println("Parameter: " + s);
        
        // Single parameter with type
        Consumer<String> oneParamTyped = (String s) -> System.out.println("Typed: " + s);
        
        // Multiple parameters
        BinaryOperator<Integer> twoParams = (a, b) -> a + b;
        
        // Multiple parameters with types
        BinaryOperator<Integer> twoParamsTyped = (Integer a, Integer b) -> a + b;
        
        // Multiple statements
        Predicate<String> multiLine = s -> {
            String trimmed = s.trim();
            return trimmed.length() > 5 && trimmed.startsWith("A");
        };
        
        // Usage
        noParams.run();
        oneParam.accept("Hello");
        System.out.println(twoParams.apply(5, 3));
        System.out.println(multiLine.test("Hello World"));
    }
}

Functional Interfaces

// Custom functional interfaces
@FunctionalInterface
public interface StringProcessor {
    String process(String input);
    
    // Can have default methods
    default String processAndLog(String input) {
        String result = process(input);
        System.out.println("Processed: " + result);
        return result;
    }
    
    // Can have static methods
    static StringProcessor toUpperCase() {
        return String::toUpperCase;
    }
}

@FunctionalInterface
public interface TriFunction<T, U, V, R> {
    R apply(T t, U u, V v);
}

// Using functional interfaces
public class FunctionalInterfaceExamples {
    
    public static void demonstrateInterfaces() {
        
        // Predicate - boolean test
        Predicate<Integer> isEven = n -> n % 2 == 0;
        Predicate<String> isLong = s -> s.length() > 10;
        
        System.out.println("4 is even: " + isEven.test(4));
        System.out.println("Hello is long: " + isLong.test("Hello"));
        
        // Consumer - consumes values
        Consumer<String> printer = System.out::println;
        Consumer<List<Integer>> listPrinter = list -> list.forEach(System.out::println);
        
        printer.accept("Hello World");
        listPrinter.accept(Arrays.asList(1, 2, 3, 4, 5));
        
        // Supplier - supplies values
        Supplier<Double> randomSupplier = Math::random;
        Supplier<LocalDateTime> nowSupplier = LocalDateTime::now;
        
        System.out.println("Random: " + randomSupplier.get());
        System.out.println("Now: " + nowSupplier.get());
        
        // Function - transformation
        Function<String, Integer> stringLength = String::length;
        Function<Integer, String> intToString = Object::toString;
        
        System.out.println("Length of Hello: " + stringLength.apply("Hello"));
        System.out.println("String of 42: " + intToString.apply(42));
        
        // UnaryOperator - specialized function
        UnaryOperator<String> upperCase = String::toUpperCase;
        UnaryOperator<Integer> square = n -> n * n;
        
        System.out.println("Upper: " + upperCase.apply("hello"));
        System.out.println("Square: " + square.apply(5));
        
        // BinaryOperator - two parameters
        BinaryOperator<Integer> add = Integer::sum;
        BinaryOperator<String> concat = String::concat;
        
        System.out.println("Add: " + add.apply(3, 7));
        System.out.println("Concat: " + concat.apply("Hello", " World"));
        
        // Custom functional interface
        StringProcessor reverser = s -> new StringBuilder(s).reverse().toString();
        StringProcessor prefixAdder = s -> "Prefix: " + s;
        
        System.out.println("Reverse: " + reverser.process("Hello"));
        System.out.println("Prefix: " + prefixAdder.process("World"));
        
        // Custom TriFunction
        TriFunction<Integer, Integer, Integer, Integer> sumThree = (a, b, c) -> a + b + c;
        System.out.println("Sum of 1,2,3: " + sumThree.apply(1, 2, 3));
    }
}

Higher-Order Functions

Functions as Parameters

public class HigherOrderFunctions {
    
    // Function that takes a function as a parameter
    public static <T, R> List<R> map(List<T> list, Function<T, R> mapper) {
        List<R> result = new ArrayList<>();
        for (T item : list) {
            result.add(mapper.apply(item));
        }
        return result;
    }
    
    // Function that takes a function as a parameter and filters
    public static <T> List<T> filter(List<T> list, Predicate<T> predicate) {
        List<T> result = new ArrayList<>();
        for (T item : list) {
            if (predicate.test(item)) {
                result.add(item);
            }
        }
        return result;
    }
    
    // Function that takes a function for reduction
    public static <T> T reduce(List<T> list, T identity, BinaryOperator<T> accumulator) {
        T result = identity;
        for (T item : list) {
            result = accumulator.apply(result, item);
        }
        return result;
    }
    
    // Function that returns a function (closure)
    public static Function<Integer, Integer> createMultiplier(int multiplier) {
        return number -> number * multiplier;
    }
    
    // Function with multiple functions as parameters
    public static <T> List<T> processList(
            List<T> list,
            Predicate<T> filter,
            Function<T, T> mapper,
            Consumer<T> consumer) {
        
        List<T> result = new ArrayList<>();
        for (T item : list) {
            if (filter.test(item)) {
                T processed = mapper.apply(item);
                consumer.accept(processed);
                result.add(processed);
            }
        }
        return result;
    }
    
    // Demonstration
    public static void demonstrateHigherOrderFunctions() {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        
        // Map: square the numbers
        List<Integer> squares = map(numbers, n -> n * n);
        System.out.println("Squares: " + squares);
        
        // Filter: only even numbers
        List<Integer> evens = filter(numbers, n -> n % 2 == 0);
        System.out.println("Evens: " + evens);
        
        // Reduce: calculate sum
        Integer sum = reduce(numbers, 0, Integer::sum);
        System.out.println("Sum: " + sum);
        
        // Closure: create multiplier functions
        Function<Integer, Integer> doubler = createMultiplier(2);
        Function<Integer, Integer> tripler = createMultiplier(3);
        
        System.out.println("Double of 5: " + doubler.apply(5));
        System.out.println("Triple of 5: " + tripler.apply(5));
        
        // Complex processing
        List<String> words = Arrays.asList("apple", "banana", "cherry", "date", "elderberry");
        
        List<String> processed = processList(
            words,
            s -> s.length() > 5,     // Filter: only long words
            String::toUpperCase,     // Map: convert to uppercase
            System.out::println      // Consumer: print each one
        );
        
        System.out.println("Processed: " + processed);
    }
}

Function Composition

public class FunctionComposition {
    
    // Function composition helper methods
    public static <T, U, R> Function<T, R> compose(Function<U, R> f, Function<T, U> g) {
        return x -> f.apply(g.apply(x));
    }
    
    public static <T> Function<T, T> compose(Function<T, T>... functions) {
        return Arrays.stream(functions)
            .reduce(Function.identity(), Function::andThen);
    }
    
    // Composition example
    public static void demonstrateComposition() {
        // Individual functions
        Function<String, String> addPrefix = s -> "Hello " + s;
        Function<String, String> addSuffix = s -> s + "!";
        Function<String, String> toUpperCase = String::toUpperCase;
        
        // Combining functions
        Function<String, String> greet = compose(addSuffix, addPrefix);
        Function<String, String> greetLoud = compose(toUpperCase, greet);
        
        System.out.println(greet.apply("World"));        // Hello World!
        System.out.println(greetLoud.apply("World"));    // HELLO WORLD!
        
        // With andThen (reversed order)
        Function<String, String> greetAndThenUpper = addPrefix.andThen(toUpperCase).andThen(addSuffix);
        System.out.println(greetAndThenUpper.apply("World")); // HELLO WORLD!
        
        // Mathematical examples
        Function<Double, Double> multiplyBy2 = x -> x * 2;
        Function<Double, Double> add3 = x -> x + 3;
        Function<Double, Double> square = x -> x * x;
        
        // (x * 2 + 3)²
        Function<Double, Double> complexOperation = compose(square, compose(add3, multiplyBy2));
        System.out.println("Complex operation (5): " + complexOperation.apply(5.0)); // ((5*2)+3)² = 13² = 169
    }
}

Stream API and Functional Programming

Stream Processing

public class StreamFunctionalProgramming {
    
    public static void demonstrateStreamProcessing() {
        List<Person> people = Arrays.asList(
            new Person("Alice", 25, "Engineering"),
            new Person("Bob", 30, "Marketing"),
            new Person("Charlie", 35, "Engineering"),
            new Person("Diana", 28, "HR"),
            new Person("Eve", 32, "Engineering")
        );
        
        // Complex stream processing
        List<String> result = people.stream()
            .filter(p -> p.getAge() >= 30)                    // Filter: age >= 30
            .filter(p -> p.getDepartment().equals("Engineering")) // Filter: Engineering
            .map(Person::getName)                            // Map: names only
            .map(String::toUpperCase)                        // Map: uppercase
            .sorted()                                         // Sort
            .collect(Collectors.toList());                    // Collect
        
        System.out.println("Filtered and mapped: " + result);
        
        // Reduction with specialized operators
        double totalAge = people.stream()
            .mapToInt(Person::getAge)
            .sum();
        
        System.out.println("Total age: " + totalAge);
        
        // Grouping
        Map<String, List<Person>> byDepartment = people.stream()
            .collect(Collectors.groupingBy(Person::getDepartment));
        
        System.out.println("By department: " + byDepartment);
        
        // Partitioning
        Map<Boolean, List<Person>> byAge = people.stream()
            .collect(Collectors.partitioningBy(p -> p.getAge() >= 30));
        
        System.out.println("By age >= 30: " + byAge);
        
        // Custom collector
        Map<String, Double> avgAgeByDept = people.stream()
            .collect(Collectors.groupingBy(
                Person::getDepartment,
                Collectors.averagingInt(Person::getAge)
            ));
        
        System.out.println("Average age by department: " + avgAgeByDept);
    }
    
    // Person class for examples
    public static class Person {
        private String name;
        private int age;
        private String department;
        
        public Person(String name, int age, String department) {
            this.name = name;
            this.age = age;
            this.department = department;
        }
        
        // Getters
        public String getName() { return name; }
        public int getAge() { return age; }
        public String getDepartment() { return department; }
        
        @Override
        public String toString() {
            return String.format("%s (%d, %s)", name, age, department);
        }
    }
}

Lazy Evaluation with Streams

public class LazyEvaluation {
    
    public static void demonstrateLazyEvaluation() {
        // Infinite stream - works through lazy evaluation
        Stream<Integer> infiniteStream = Stream.iterate(0, n -> n + 1);
        
        // Only the first 10 elements are computed
        List<Integer> firstTen = infiniteStream
            .limit(10)
            .collect(Collectors.toList());
        
        System.out.println("First 10: " + firstTen);
        
        // Fibonacci numbers with lazy evaluation
        Stream<Long> fibonacci = Stream.generate(new FibonacciSupplier());
        
        List<Long> firstFibonacci = fibonacci
            .limit(10)
            .collect(Collectors.toList());
        
        System.out.println("First 10 Fibonacci: " + firstFibonacci);
        
        // Lazy filter and map
        List<Integer> processed = Stream.iterate(1, n -> n + 1)
            .filter(LazyEvaluation::isPrime)      // Prime numbers only
            .map(n -> n * n)                      // Square
            .limit(5)                             // First 5 only
            .collect(Collectors.toList());
        
        System.out.println("First 5 prime squares: " + processed);
    }
    
    private static boolean isPrime(int n) {
        if (n <= 1) return false;
        if (n == 2) return true;
        if (n % 2 == 0) return false;
        
        for (int i = 3; i * i <= n; i += 2) {
            if (n % i == 0) return false;
        }
        return true;
    }
    
    // Fibonacci supplier
    private static class FibonacciSupplier implements Supplier<Long> {
        private long a = 0;
        private long b = 1;
        
        @Override
        public Long get() {
            long result = a;
            long next = a + b;
            a = b;
            b = next;
            return result;
        }
    }
}

Functional Programming in Python

Lambda and Higher-Order Functions

from functools import reduce, partial
from typing import List, Callable, Any

# Lambda expressions
def lambda_examples():
    # Simple lambda functions
    square = lambda x: x ** 2
    add = lambda x, y: x + y
    is_even = lambda x: x % 2 == 0
    
    print(f"Square of 5: {square(5)}")
    print(f"Add 3 + 7: {add(3, 7)}")
    print(f"Is 4 even: {is_even(4)}")
    
    # Lambda with built-in functions
    numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    
    # Map with lambda
    squares = list(map(lambda x: x ** 2, numbers))
    print(f"Squares: {squares}")
    
    # Filter with lambda
    evens = list(filter(lambda x: x % 2 == 0, numbers))
    print(f"Evens: {evens}")
    
    # Reduce with lambda
    sum_all = reduce(lambda x, y: x + y, numbers)
    print(f"Sum: {sum_all}")
    
    # Sorting with lambda
    words = ["apple", "banana", "cherry", "date"]
    sorted_by_length = sorted(words, key=lambda x: len(x))
    print(f"Sorted by length: {sorted_by_length}")

# Higher-order functions
def higher_order_functions():
    # Function that takes a function as parameter
    def apply_operation(numbers: List[int], operation: Callable[[int], int]) -> List[int]:
        return [operation(num) for num in numbers]
    
    # Function that returns a function
    def create_multiplier(factor: int) -> Callable[[int], int]:
        return lambda x: x * factor
    
    # Usage
    numbers = [1, 2, 3, 4, 5]
    
    squares = apply_operation(numbers, lambda x: x ** 2)
    cubes = apply_operation(numbers, lambda x: x ** 3)
    
    print(f"Squares: {squares}")
    print(f"Cubes: {cubes}")
    
    # Closure
    doubler = create_multiplier(2)
    tripler = create_multiplier(3)
    
    print(f"Double of 5: {doubler(5)}")
    print(f"Triple of 5: {tripler(5)}")
    
    # Function composition
    def compose(f: Callable, g: Callable) -> Callable:
        return lambda x: f(g(x))
    
    add_one = lambda x: x + 1
    multiply_by_two = lambda x: x * 2
    
    add_then_multiply = compose(multiply_by_two, add_one)
    multiply_then_add = compose(add_one, multiply_by_two)
    
    print(f"Add then multiply (5): {add_then_multiply(5)}")  # (5 + 1) * 2 = 12
    print(f"Multiply then add (5): {multiply_then_add(5)}")  # (5 * 2) + 1 = 11

# Partial functions
def partial_functions():
    def multiply(x: int, y: int) -> int:
        return x * y
    
    def power(base: int, exponent: int) -> int:
        return base ** exponent
    
    # Partial application
    double = partial(multiply, 2)
    triple = partial(multiply, 3)
    
    square = partial(power, 2)
    cube = partial(power, 3)
    
    print(f"Double of 5: {double(5)}")
    print(f"Triple of 5: {triple(5)}")
    print(f"Square of 5: {square(5)}")
    print(f"Cube of 5: {cube(5)}")

# List comprehensions (functional alternative)
def list_comprehensions():
    numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    
    # Traditional with map/filter
    squares_even = list(filter(lambda x: x % 2 == 0, map(lambda x: x ** 2, numbers)))
    
    # With list comprehension
    squares_even_comp = [x ** 2 for x in numbers if x % 2 == 0]
    
    print(f"Squares of evens (map/filter): {squares_even}")
    print(f"Squares of evens (comprehension): {squares_even_comp}")
    
    # Nested comprehensions
    matrix = [[i * j for j in range(1, 4)] for i in range(1, 4)]
    print(f"Multiplication table: {matrix}")
    
    # Dictionary comprehension
    word_lengths = {word: len(word) for word in ["apple", "banana", "cherry"]}
    print(f"Word lengths: {word_lengths}")

# Decorators (higher-order functions)
def decorators():
    def timing_decorator(func):
        import time
        def wrapper(*args, **kwargs):
            start = time.time()
            result = func(*args, **kwargs)
            end = time.time()
            print(f"{func.__name__} took {end - start:.4f} seconds")
            return result
        return wrapper
    
    def memoization_decorator(func):
        cache = {}
        def wrapper(*args):
            if args in cache:
                return cache[args]
            result = func(*args)
            cache[args] = result
            return result
        return wrapper
    
    @timing_decorator
    @memoization_decorator
    def fibonacci(n: int) -> int:
        if n <= 1:
            return n
        return fibonacci(n - 1) + fibonacci(n - 2)
    
    print(f"Fibonacci(10): {fibonacci(10)}")
    print(f"Fibonacci(15): {fibonacci(15)}")  # Faster through memoization

# Run all examples
if __name__ == "__main__":
    print("=== Lambda Examples ===")
    lambda_examples()
    
    print("\n=== Higher-Order Functions ===")
    higher_order_functions()
    
    print("\n=== Partial Functions ===")
    partial_functions()
    
    print("\n=== List Comprehensions ===")
    list_comprehensions()
    
    print("\n=== Decorators ===")
    decorators()

Immutability and Pure Functions

Immutable Data Structures

public class ImmutableDataStructures {
    
    // Immutable Person class
    public static final class Person {
        private final String name;
        private final int age;
        private final List<String> hobbies;
        
        public Person(String name, int age, List<String> hobbies) {
            this.name = Objects.requireNonNull(name);
            this.age = age;
            this.hobbies = List.copyOf(hobbies); // Defensive copy
        }
        
        // Getters - no setters
        public String getName() { return name; }
        public int getAge() { return age; }
        public List<String> getHobbies() { return hobbies; }
        
        // Methods that return new instances
        public Person withAge(int newAge) {
            return new Person(this.name, newAge, this.hobbies);
        }
        
        public Person withName(String newName) {
            return new Person(newName, this.age, this.hobbies);
        }
        
        public Person addHobby(String hobby) {
            List<String> newHobbies = new ArrayList<>(this.hobbies);
            newHobbies.add(hobby);
            return new Person(this.name, this.age, newHobbies);
        }
        
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Person person = (Person) o;
            return age == person.age && 
                   Objects.equals(name, person.name) && 
                   Objects.equals(hobbies, person.hobbies);
        }
        
        @Override
        public int hashCode() {
            return Objects.hash(name, age, hobbies);
        }
        
        @Override
        public String toString() {
            return String.format("Person{name='%s', age=%d, hobbies=%s}", name, age, hobbies);
        }
    }
    
    // Functional operations on immutable data
    public static void demonstrateImmutability() {
        List<Person> people = Arrays.asList(
            new Person("Alice", 25, Arrays.asList("reading", "swimming")),
            new Person("Bob", 30, Arrays.asList("gaming", "cooking")),
            new Person("Charlie", 35, Arrays.asList("music", "travel"))
        );
        
        // Original list remains unchanged
        List<Person> olderPeople = people.stream()
            .map(person -> person.withAge(person.getAge() + 1))
            .collect(Collectors.toList());
        
        System.out.println("Original: " + people);
        System.out.println("Aged by 1: " + olderPeople);
        
        // Functional transformation
        List<String> hobbies = people.stream()
            .flatMap(person -> person.getHobbies().stream())
            .distinct()
            .sorted()
            .collect(Collectors.toList());
        
        System.out.println("All hobbies: " + hobbies);
    }
}

Pure Function Examples

public class PureFunctions {
    
    // Pure function - no side effects
    public static int calculateArea(int width, int height) {
        if (width < 0 || height < 0) {
            throw new IllegalArgumentException("Dimensions must be positive");
        }
        return width * height;
    }
    
    // Pure function for string processing
    public static String formatFullName(String firstName, String lastName) {
        if (firstName == null || lastName == null) {
            return "";
        }
        return String.format("%s %s", 
            firstName.trim().toUpperCase(), 
            lastName.trim().toUpperCase()
        );
    }
    
    // Pure function for list processing
    public static List<Integer> filterAndSquare(List<Integer> numbers, Predicate<Integer> predicate) {
        return numbers.stream()
            .filter(predicate)
            .map(n -> n * n)
            .collect(Collectors.toList());
    }
    
    // Impure function - for comparison
    private static int counter = 0;
    
    public static int incrementCounter() {
        return counter++; // Side effect
    }
    
    // Demonstration
    public static void demonstratePureFunctions() {
        // Pure function - always the same result
        int area1 = calculateArea(5, 3);
        int area2 = calculateArea(5, 3);
        System.out.println("Areas are equal: " + (area1 == area2)); // true
        
        // Pure function with strings
        String fullName1 = formatFullName("John", "Doe");
        String fullName2 = formatFullName("John", "Doe");
        System.out.println("Names are equal: " + fullName1.equals(fullName2)); // true
        
        // Pure function with lists
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        List<Integer> evenSquares = filterAndSquare(numbers, n -> n % 2 == 0);
        System.out.println("Even squares: " + evenSquares);
        
        // Impure function - different results
        int count1 = incrementCounter();
        int count2 = incrementCounter();
        System.out.println("Counts are different: " + (count1 != count2)); // true
    }
}

Monads and Functional Concepts

Optional Monad

public class MonadExamples {
    
    // Optional for null-safe operations
    public static void demonstrateOptional() {
        Optional<String> optional = Optional.of("Hello World");
        
        // Map transformation
        Optional<Integer> length = optional.map(String::length);
        System.out.println("Length: " + length.orElse(0));
        
        // Filter
        Optional<String> filtered = optional.filter(s -> s.length() > 5);
        System.out.println("Filtered: " + filtered.orElse("Too short"));
        
        // FlatMap for nested optionals
        Optional<String> upperCase = optional.flatMap(s -> 
            s.length() > 5 ? Optional.of(s.toUpperCase()) : Optional.empty()
        );
        System.out.println("Upper case: " + upperCase.orElse("Not available"));
        
        // Null-safe chain
        String result = Optional.ofNullable(getUser())
            .map(User::getAddress)
            .map(Address::getCity)
            .orElse("Unknown");
        
        System.out.println("City: " + result);
    }
    
    // Either monad (simplified implementation)
    public static class Either<L, R> {
        private final L left;
        private final R right;
        
        private Either(L left, R right) {
            this.left = left;
            this.right = right;
        }
        
        public static <L, R> Either<L, R> left(L value) {
            return new Either<>(value, null);
        }
        
        public static <L, R> Either<L, R> right(R value) {
            return new Either<>(null, value);
        }
        
        public boolean isLeft() { return left != null; }
        public boolean isRight() { return right != null; }
        
        public L getLeft() { return left; }
        public R getRight() { return right; }
        
        public <T> Either<L, T> map(Function<R, T> mapper) {
            if (isRight()) {
                return Either.right(mapper.apply(right));
            }
            return Either.left(left);
        }
        
        public <T> Either<L, T> flatMap(Function<R, Either<L, T>> mapper) {
            if (isRight()) {
                return mapper.apply(right);
            }
            return Either.left(left);
        }
    }
    
    // Either usage
    public static Either<String, Integer> parseNumber(String input) {
        try {
            return Either.right(Integer.parseInt(input));
        } catch (NumberFormatException e) {
            return Either.left("Invalid number: " + input);
        }
    }
    
    public static void demonstrateEither() {
        Either<String, Integer> result1 = parseNumber("123");
        Either<String, Integer> result2 = parseNumber("abc");
        
        result1.map(n -> n * 2)
               .map(Object::toString)
               .ifRight(System.out::println)
               .ifLeft(System.err::println);
        
        result2.map(n -> n * 2)
               .map(Object::toString)
               .ifRight(System.out::println)
               .ifLeft(System.err::println);
    }
    
    // Helper methods for Either
    private static <T> void ifRight(Either<String, T> either, Consumer<T> consumer) {
        if (either.isRight()) {
            consumer.accept(either.getRight());
        }
    }
    
    private static <T> void ifLeft(Either<String, T> either, Consumer<String> consumer) {
        if (either.isLeft()) {
            consumer.accept(either.getLeft());
        }
    }
    
    // Dummy classes for examples
    private static User getUser() {
        return new User("John", new Address("123 Main St", "New York"));
    }
    
    private static class User {
        private String name;
        private Address address;
        
        public User(String name, Address address) {
            this.name = name;
            this.address = address;
        }
        
        public Address getAddress() { return address; }
    }
    
    private static class Address {
        private String street;
        private String city;
        
        public Address(String street, String city) {
            this.street = street;
            this.city = city;
        }
        
        public String getCity() { return city; }
    }
}

Performance and Best Practices

Functional Programming Performance

public class FunctionalPerformance {
    
    // Traditional loop
    public static int traditionalSum(List<Integer> numbers) {
        int sum = 0;
        for (int num : numbers) {
            sum += num;
        }
        return sum;
    }
    
    // Functional variant with Stream
    public static int functionalSum(List<Integer> numbers) {
        return numbers.stream().reduce(0, Integer::sum);
    }
    
    // Parallel Stream for large datasets
    public static int parallelSum(List<Integer> numbers) {
        return numbers.parallelStream().reduce(0, Integer::sum);
    }
    
    // Performance comparison
    public static void performanceComparison() {
        List<Integer> numbers = IntStream.range(0, 1_000_000)
            .boxed()
            .collect(Collectors.toList());
        
        // Warm-up
        traditionalSum(numbers);
        functionalSum(numbers);
        parallelSum(numbers);
        
        // Benchmark
        long start = System.nanoTime();
        int result1 = traditionalSum(numbers);
        long traditionalTime = System.nanoTime() - start;
        
        start = System.nanoTime();
        int result2 = functionalSum(numbers);
        long functionalTime = System.nanoTime() - start;
        
        start = System.nanoTime();
        int result3 = parallelSum(numbers);
        long parallelTime = System.nanoTime() - start;
        
        System.out.println("Results equal: " + (result1 == result2 && result2 == result3));
        System.out.println("Traditional: " + (traditionalTime / 1_000_000) + " ms");
        System.out.println("Functional: " + (functionalTime / 1_000_000) + " ms");
        System.out.println("Parallel: " + (parallelTime / 1_000_000) + " ms");
    }
    
    // Best practices
    public static void bestPractices() {
        List<String> words = Arrays.asList("apple", "banana", "cherry", "date", "elderberry");
        
        // 1. Method References instead of lambdas when possible
        List<Integer> lengths1 = words.stream()
            .map(s -> s.length())           // Lambda
            .collect(Collectors.toList());
        
        List<Integer> lengths2 = words.stream()
            .map(String::length)            // Method Reference
            .collect(Collectors.toList());
        
        // 2. Primitive Streams for performance
        int sum = words.stream()
            .mapToInt(String::length)       // IntStream instead of Stream<Integer>
            .sum();
        
        // 3. Take advantage of lazy evaluation
        Optional<String> firstLong = words.stream()
            .filter(s -> s.length() > 5)
            .findFirst();                    // Stops after first match
        
        // 4. Prefer immutable operations
        List<String> processed = words.stream()
            .map(String::toUpperCase)
            .filter(s -> s.startsWith("A"))
            .collect(Collectors.toList());  // New list instead of modification
        
        System.out.println("Best practices completed");
    }
}

Exam-Relevant Concepts

Key Functional Programming Concepts

  1. Pure Functions: No side effects, deterministic
  2. Immutability: Unchanging data structures
  3. Higher-Order Functions: Functions as parameters or return values
  4. Function Composition: Combining functions
  5. Lazy Evaluation: Deferred computation
  6. Monads: Optional, Either, Stream

Typical Exam Questions

  1. Implement a pure function
  2. Explain function composition
  3. Compare imperative versus functional approaches
  4. Implement a higher-order function
  5. Describe the benefits of functional programming

Summary

Functional programming offers numerous advantages:

  • Testability: Pure functions are straightforward to test
  • Parallelizability: The absence of side effects makes concurrent programming simpler
  • Maintainability: Immutable data reduces bugs
  • Readability: Declarative code is often clearer

Combining object-oriented and functional programming enables you to build robust, scalable, and maintainable software architectures.

Book Recommendation

Keine Bücher für Kategorie "programming-languages" gefunden.

More Functional Programming Articles

Functional programming is an important paradigm in modern software development. The following articles help you master all aspects of functional programming.

Foundations and Concepts

Back to Blog
Share:

Related Posts