Funktionale Programmierung: Lambda, Functional Interfaces & Higher-Order Functions
Funktionale Programmierung ist ein Programmierparadigma, das Funktionen als primäre Bausteine behandelt. Es fördert unveränderliche Daten und reine Funktionen für bessere Testbarkeit und Parallelisierbarkeit.
Grundlagen der Funktionalen Programmierung
Kernprinzipien
- Pure Functions: Funktionen ohne Seiteneffekte
- Immutability: Unveränderliche Datenstrukturen
- First-Class Functions: Funktionen als Werte
- Higher-Order Functions: Funktionen, die andere Funktionen als Parameter nehmen
- Function Composition: Kombination von Funktionen
Reine vs Unreine Funktionen
public class FunctionExamples {
// Reine Funktion - keine Seiteneffekte
public static int add(int a, int b) {
return a + b;
// Immer gleiches Ergebnis für gleiche Eingaben
// Keine Seiteneffekte
}
// Unreine Funktion - mit Seiteneffekten
private static int counter = 0;
public static int addToCounter(int value) {
counter += value; // Seiteneffekt: ändert globalen Zustand
return counter;
// Verschiedene Ergebnisse für gleiche Eingaben
}
// Reine Funktion für String-Verarbeitung
public static String capitalize(String input) {
if (input == null) return null;
return input.toUpperCase();
}
// Unreine Funktion mit externer Abhängigkeit
public static String getCurrentTimeFormatted() {
return DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(LocalDateTime.now());
// Hängt von aktueller Zeit ab
}
}
Lambda Ausdrücke in Java
Syntax und Grundlagen
public class LambdaBasics {
// Traditionelle anonyme Klasse
public static void traditionalApproach() {
Runnable runnable1 = new Runnable() {
@Override
public void run() {
System.out.println("Traditional approach");
}
};
runnable1.run();
}
// Lambda Ausdruck
public static void lambdaApproach() {
Runnable runnable2 = () -> System.out.println("Lambda approach");
runnable2.run();
}
// Lambda mit verschiedenen Syntax-Varianten
public static void lambdaSyntaxExamples() {
// Keine Parameter
Runnable noParams = () -> System.out.println("No parameters");
// Ein Parameter
Consumer<String> oneParam = s -> System.out.println("Parameter: " + s);
// Ein Parameter mit Typ
Consumer<String> oneParamTyped = (String s) -> System.out.println("Typed: " + s);
// Mehrere Parameter
BinaryOperator<Integer> twoParams = (a, b) -> a + b;
// Mehrere Parameter mit Typen
BinaryOperator<Integer> twoParamsTyped = (Integer a, Integer b) -> a + b;
// Mehrere Zeilen
Predicate<String> multiLine = s -> {
String trimmed = s.trim();
return trimmed.length() > 5 && trimmed.startsWith("A");
};
// Verwendung
noParams.run();
oneParam.accept("Hello");
System.out.println(twoParams.apply(5, 3));
System.out.println(multiLine.test("Hello World"));
}
}
Functional Interfaces
// Eigene Functional Interfaces
@FunctionalInterface
public interface StringProcessor {
String process(String input);
// Kann default Methoden haben
default String processAndLog(String input) {
String result = process(input);
System.out.println("Processed: " + result);
return result;
}
// Kann static Methoden haben
static StringProcessor toUpperCase() {
return String::toUpperCase;
}
}
@FunctionalInterface
public interface TriFunction<T, U, V, R> {
R apply(T t, U u, V v);
}
// Verwendung von 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 - Konsumiert Werte
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 - Liefert Werte
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 - Spezielle 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 - Zwei Parameter
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"));
// Eigenes 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
Funktionen als Parameter
public class HigherOrderFunctions {
// Funktion, die eine Funktion als Parameter nimmt
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;
}
// Funktion, die eine Funktion als Parameter nimmt und filtert
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;
}
// Funktion, die eine Funktion für Reduktion nimmt
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;
}
// Funktion, die eine Funktion zurückgibt (Closure)
public static Function<Integer, Integer> createMultiplier(int multiplier) {
return number -> number * multiplier;
}
// Funktion mit mehreren Funktionen als Parameter
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: Zahlen quadrieren
List<Integer> squares = map(numbers, n -> n * n);
System.out.println("Squares: " + squares);
// Filter: Nur gerade Zahlen
List<Integer> evens = filter(numbers, n -> n % 2 == 0);
System.out.println("Evens: " + evens);
// Reduce: Summe berechnen
Integer sum = reduce(numbers, 0, Integer::sum);
System.out.println("Sum: " + sum);
// Closure: Multiplier-Funktion erstellen
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));
// Komplexe Verarbeitung
List<String> words = Arrays.asList("apple", "banana", "cherry", "date", "elderberry");
List<String> processed = processList(
words,
s -> s.length() > 5, // Filter: nur lange Wörter
String::toUpperCase, // Map: in Großbuchstaben
System.out::println // Consumer: ausgeben
);
System.out.println("Processed: " + processed);
}
}
Function Composition
public class FunctionComposition {
// Function Composition Hilfsmethoden
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);
}
// Beispiel für Composition
public static void demonstrateComposition() {
// Einzelne Funktionen
Function<String, String> addPrefix = s -> "Hello " + s;
Function<String, String> addSuffix = s -> s + "!";
Function<String, String> toUpperCase = String::toUpperCase;
// Funktionen kombinieren
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!
// Mit andThen (reihenfolge umgekehrt)
Function<String, String> greetAndThenUpper = addPrefix.andThen(toUpperCase).andThen(addSuffix);
System.out.println(greetAndThenUpper.apply("World")); // HELLO WORLD!
// Mathematische Beispiele
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 und Funktionale Programmierung
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")
);
// Komplexe Stream-Verarbeitung
List<String> result = people.stream()
.filter(p -> p.getAge() >= 30) // Filter: Alter >= 30
.filter(p -> p.getDepartment().equals("Engineering")) // Filter: Engineering
.map(Person::getName) // Map: nur Namen
.map(String::toUpperCase) // Map: Großbuchstaben
.sorted() // Sortieren
.collect(Collectors.toList()); // Sammeln
System.out.println("Filtered and mapped: " + result);
// Reduktion mit spezialisierten Operatoren
double totalAge = people.stream()
.mapToInt(Person::getAge)
.sum();
System.out.println("Total age: " + totalAge);
// Gruppierung
Map<String, List<Person>> byDepartment = people.stream()
.collect(Collectors.groupingBy(Person::getDepartment));
System.out.println("By department: " + byDepartment);
// Partitionierung
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 Klasse für Beispiele
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;
}
// Getter
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 mit Streams
public class LazyEvaluation {
public static void demonstrateLazyEvaluation() {
// Unendlicher Stream - funktioniert durch Lazy Evaluation
Stream<Integer> infiniteStream = Stream.iterate(0, n -> n + 1);
// Nur erste 10 Elemente werden berechnet
List<Integer> firstTen = infiniteStream
.limit(10)
.collect(Collectors.toList());
System.out.println("First 10: " + firstTen);
// Fibonacci Zahlen mit 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 und Map
List<Integer> processed = Stream.iterate(1, n -> n + 1)
.filter(LazyEvaluation::isPrime) // Nur Primzahlen
.map(n -> n * n) // Quadrieren
.limit(5) // Nur erste 5
.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;
}
}
}
Funktionale Programmierung in Python
Lambda und Higher-Order Functions
from functools import reduce, partial
from typing import List, Callable, Any
# Lambda Ausdrücke
def lambda_examples():
# Einfache Lambda-Funktionen
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 mit eingebauten Funktionen
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# Map mit Lambda
squares = list(map(lambda x: x ** 2, numbers))
print(f"Squares: {squares}")
# Filter mit Lambda
evens = list(filter(lambda x: x % 2 == 0, numbers))
print(f"Evens: {evens}")
# Reduce mit Lambda
sum_all = reduce(lambda x, y: x + y, numbers)
print(f"Sum: {sum_all}")
# Sortieren mit 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():
# Funktion, die Funktion als Parameter nimmt
def apply_operation(numbers: List[int], operation: Callable[[int], int]) -> List[int]:
return [operation(num) for num in numbers]
# Funktion, die Funktion zurückgibt
def create_multiplier(factor: int) -> Callable[[int], int]:
return lambda x: x * factor
# Verwendung
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 (funktionale Alternative)
def list_comprehensions():
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# Traditionell mit map/filter
squares_even = list(filter(lambda x: x % 2 == 0, map(lambda x: x ** 2, numbers)))
# Mit 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)}") # Schneller durch Memoization
# Alle Beispiele ausführen
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 und Pure Functions
Unveränderliche Datenstrukturen
public class ImmutableDataStructures {
// Unveränderliche Person Klasse
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
}
// Getter - keine Setter!
public String getName() { return name; }
public int getAge() { return age; }
public List<String> getHobbies() { return hobbies; }
// Methoden erstellen neue Instanzen
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);
}
}
// Funktionale Operationen auf unveränderlichen Daten
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"))
);
// Alte Liste bleibt unverändert
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);
// Funktionale Transformation
List<String> hobbies = people.stream()
.flatMap(person -> person.getHobbies().stream())
.distinct()
.sorted()
.collect(Collectors.toList());
System.out.println("All hobbies: " + hobbies);
}
}
Pure Function Beispiele
public class PureFunctions {
// Reine Funktion - keine Seiteneffekte
public static int calculateArea(int width, int height) {
if (width < 0 || height < 0) {
throw new IllegalArgumentException("Dimensions must be positive");
}
return width * height;
}
// Reine Funktion für String-Verarbeitung
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()
);
}
// Reine Funktion für Listenverarbeitung
public static List<Integer> filterAndSquare(List<Integer> numbers, Predicate<Integer> predicate) {
return numbers.stream()
.filter(predicate)
.map(n -> n * n)
.collect(Collectors.toList());
}
// Unreine Funktion - zum Vergleich
private static int counter = 0;
public static int incrementCounter() {
return counter++; // Seiteneffekt!
}
// Demonstration
public static void demonstratePureFunctions() {
// Pure Function - immer gleiches Ergebnis
int area1 = calculateArea(5, 3);
int area2 = calculateArea(5, 3);
System.out.println("Areas are equal: " + (area1 == area2)); // true
// Pure Function mit Strings
String fullName1 = formatFullName("John", "Doe");
String fullName2 = formatFullName("John", "Doe");
System.out.println("Names are equal: " + fullName1.equals(fullName2)); // true
// Pure Function mit Listen
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);
// Unreine Funktion - verschiedene Ergebnisse
int count1 = incrementCounter();
int count2 = incrementCounter();
System.out.println("Counts are different: " + (count1 != count2)); // true
}
}
Monaden und Funktionale Konzepte
Optional Monad
public class MonadExamples {
// Optional für null-sichere Operationen
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 für verschachtelte 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-sichere Kette
String result = Optional.ofNullable(getUser())
.map(User::getAddress)
.map(Address::getCity)
.orElse("Unknown");
System.out.println("City: " + result);
}
// Either Monad (vereinfachte Implementierung)
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 Verwendung
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);
}
// Hilfsmethoden für 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 Klassen für Beispiele
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 und Best Practices
Funktionale Programmierung Performance
public class FunctionalPerformance {
// Traditionelle Schleife
public static int traditionalSum(List<Integer> numbers) {
int sum = 0;
for (int num : numbers) {
sum += num;
}
return sum;
}
// Funktionale Variante mit Stream
public static int functionalSum(List<Integer> numbers) {
return numbers.stream().reduce(0, Integer::sum);
}
// Parallel Stream für große Datenmengen
public static int parallelSum(List<Integer> numbers) {
return numbers.parallelStream().reduce(0, Integer::sum);
}
// Performance Vergleich
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 statt Lambda wenn möglich
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 für Performance
int sum = words.stream()
.mapToInt(String::length) // IntStream statt Stream<Integer>
.sum();
// 3. Lazy Evaluation nutzen
Optional<String> firstLong = words.stream()
.filter(s -> s.length() > 5)
.findFirst(); // Stoppt nach erstem Treffer
// 4. Unveränderliche Operationen bevorzugen
List<String> processed = words.stream()
.map(String::toUpperCase)
.filter(s -> s.startsWith("A"))
.collect(Collectors.toList()); // Neue Liste statt modification
System.out.println("Best practices completed");
}
}
Prüfungsrelevante Konzepte
Wichtige funktionale Konzepte
- Pure Functions: Keine Seiteneffekte, deterministisch
- Immutability: Unveränderliche Datenstrukturen
- Higher-Order Functions: Funktionen als Parameter/Rückgabewerte
- Function Composition: Kombination von Funktionen
- Lazy Evaluation: Verzögerte Auswertung
- Monads: Optional, Either, Stream
Typische Prüfungsaufgaben
- Implementieren Sie eine pure Funktion
- Erklären Sie Function Composition
- Vergleichen Sie imperative vs funktionale Ansätze
- Implementieren Sie einen Higher-Order Function
- Beschreiben Sie Vorteile der funktionalen Programmierung
Zusammenfassung
Funktionale Programmierung bietet viele Vorteile:
- Testbarkeit: Reine Funktionen sind leicht zu testen
- Parallelisierbarkeit: Keine Seiteneffekte erleichtern nebenläufige Programmierung
- Wartbarkeit: Unveränderliche Daten reduzieren Fehler
- Lesbarkeit: Deklarativer Code ist oft klarer
Die Kombination von objektorientierter und funktionaler Programmierung ermöglicht robuste, skalierbare und wartbare Softwarearchitekturen.
Buchempfehlung
Programmiersprachen
Bücher über Python, JavaScript, Rust, Go und mehr
Coding mit KI: Das Praxisbuch für die Softwareentwicklung. So hilft Künstliche Intelligenz bei IT-Projekten. Prompt Engineering, Retrieval Augmented Generation u. v. m.
Bei Amazon ansehenAffiliate-Link: Bei einem Kauf erhalten wir möglicherweise eine Provision.
Python 3: Das umfassende Handbuch
Bei Amazon ansehenAffiliate-Link: Bei einem Kauf erhalten wir möglicherweise eine Provision.
C# mit .NET: Das umfassende Handbuch zu Sprachgrundlagen, Programmiertechniken und .NET-Technologien
Bei Amazon ansehenAffiliate-Link: Bei einem Kauf erhalten wir möglicherweise eine Provision.
Java ist auch eine Insel: Einführung, Ausbildung, Praxis
Bei Amazon ansehenAffiliate-Link: Bei einem Kauf erhalten wir möglicherweise eine Provision.
Einstieg in C: Für Programmiereinsteiger geeignet. Alle Grundlagen, spannende Beispielprojekte, Praxistipps
Bei Amazon ansehenAffiliate-Link: Bei einem Kauf erhalten wir möglicherweise eine Provision.
Clean Code - Refactoring, Patterns, Testen und Techniken für sauberen Code: Deutsche Ausgabe
Bei Amazon ansehenAffiliate-Link: Bei einem Kauf erhalten wir möglicherweise eine Provision.
Clean Code für Dummies: Besser programmieren. Professionelle Softwareentwicklung.
Bei Amazon ansehenAffiliate-Link: Bei einem Kauf erhalten wir möglicherweise eine Provision.
Besser coden: Best Practices für Clean Code. Das ideale Buch für die professionelle Softwareentwicklung
Bei Amazon ansehenAffiliate-Link: Bei einem Kauf erhalten wir möglicherweise eine Provision.
Docker: Das Praxisbuch für Entwickler und DevOps-Teams. Grundlagen, Einstieg, Konzepte. Für Windows, macOS und Linux. Ausgabe 2026
Bei Amazon ansehenAffiliate-Link: Bei einem Kauf erhalten wir möglicherweise eine Provision.
Das Komplette Docker Handbuch: Containerisierte Anwendungen Erstellen, Verteilen, Ausführen
Bei Amazon ansehenAffiliate-Link: Bei einem Kauf erhalten wir möglicherweise eine Provision.
Langlebige Software-Architekturen: Technische Schulden analysieren, begrenzen und abbauen Broschiert – 18. April 2024
Bei Amazon ansehenAffiliate-Link: Bei einem Kauf erhalten wir möglicherweise eine Provision.
Handbuch für Softwareentwickler: Das Standardwerk für professionelles Software Engineering
Bei Amazon ansehenAffiliate-Link: Bei einem Kauf erhalten wir möglicherweise eine Provision.
Fullstack-Entwicklung: Das Handbuch für Webentwickler in neuer Auflage. Über 800 Seiten Roadmap
Bei Amazon ansehenAffiliate-Link: Bei einem Kauf erhalten wir möglicherweise eine Provision.
Weitere Funktionale Programmierung Artikel
Funktionale Programmierung ist ein wichtiges Paradigma der modernen Softwareentwicklung. Die folgenden Artikel helfen dir, alle Aspekte der funktionalen Programmierung zu meistern.
Grundlagen und Konzepte
- Funktionale Programmierung: Lambda Ausdrücke - Lambda-Ausdrücke und Functional Interfaces
- Funktionale Programmierung: Lambda mit Streams - Stream-API und funktionale Operationen
- Funktionale Programmierung: Lambda Grundlagen - Grundlagen der funktionalen Programmierung












