Lesson Completion
Back to course

Functional Interfaces: Lambda Target Types

Beginner
12 minutes4.7Java

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

  • In a Nutshell: Functional interface = interface with exactly one abstract method (SAM). @FunctionalInterface annotation (optional, recommended). Lambda target type—defines what lambda does. Built-in (java.util.function): Predicate (test→boolean), Function<T,R> (apply→transform), Consumer (accept→side effect), Supplier (get→provide value), UnaryOperator (same type I/O), BinaryOperator (combine two).
  • Combinable: and(), or(), compose(), andThen().
  • Primitive variants: IntPredicate, LongFunction (avoid boxing)!

Think of electrical outlet. Functional interface = outlet shape (single socket). Lambda = plug (fits the socket). Predicate = quality inspector (pass/fail). Function = assembly machine (input→output). Consumer = shredder (input, no output). Supplier = vending machine (no input, output)!


2. Conceptual Clarity (The "Simple" Tier)

💡 The Analogy: Tool Interfaces

  • Predicate: Quality control gate ("Is this acceptable? Yes/No")
  • Function: Transformer ("Convert this to that")
  • Consumer: Sink ("Process this, no return")
  • Supplier: Source ("Give me something, no input needed")

Functional Interface Hierarchy

graph TD A[Functional Interfaces] --> B[Predicate\u003cT\u003e] A --> C[Function\u003cT,R\u003e] A --> D[Consumer\u003cT\u003e] A --> E[Supplier\u003cT\u003e] C --> F[UnaryOperator\u003cT\u003e] C --> G[BinaryOperator\u003cT\u003e] B -->|test→boolean| H[Filtering] C -->|apply→R| I[Transformation] D -->|accept→void| J[Side Effects] E -->|get→T| K[Lazy Init]

3. Technical Mastery (The "Deep Dive")

Core Functional Interfaces

InterfaceMethodInputOutputUse For
Predicatetest(T)TbooleanFiltering
Function<T,R>apply(T)TRMapping
Consumeraccept(T)TvoidforEach
Supplierget()noneTFactories
UnaryOperatorapply(T)TTTransform same type
BinaryOperatorapply(T,T)T, TTReduce
BiFunction<T,U,R>apply(T,U)T, URTwo inputs

4. Interactive & Applied Code

java
import java.util.*; import java.util.function.*; public class FunctionalInterfacesDemo { public static void main(String[] args) { demonstratePredicate(); demonstrateFunction(); demonstrateConsumer(); demonstrateSupplier(); demonstrateOperators(); demonstrateCombining(); } // Predicate<T>: T -> boolean (filtering) static void demonstratePredicate() { System.out.println("=== PREDICATE ==="); List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6); Predicate<Integer> isEven = n -> n % 2 == 0; Predicate<Integer> isPositive = n -> n > 0; Predicate<Integer> greaterThan3 = n -> n > 3; System.out.println("Is 4 even? " + isEven.test(4)); // Combining predicates Predicate<Integer> evenAndGreater = isEven.and(greaterThan3); System.out.println("Even AND >3:"); numbers.stream().filter(evenAndGreater).forEach(System.out::println); // Negate Predicate<Integer> isOdd = isEven.negate(); System.out.println("Odd numbers:"); numbers.stream().filter(isOdd).forEach(System.out::println); } // Function<T, R>: T -> R (transformation) static void demonstrateFunction() { System.out.println("\n=== FUNCTION ==="); Function<String, Integer> length = s -> s.length(); Function<Integer, Integer> square = n -> n * n; Function<String, String> upper = String::toUpperCase; System.out.println("Length of 'hello': " + length.apply("hello")); // Chaining functions Function<String, Integer> lengthSquared = length.andThen(square); System.out.println("Length squared of 'abc': " + lengthSquared.apply("abc")); // Compose (reverse order) Function<Integer, String> repeat = n -> "x".repeat(n); Function<String, String> upperRepeat = upper.compose(repeat); System.out.println("Upper compose repeat: " + upperRepeat.apply("hello")); } // Consumer<T>: T -> void (side effects) static void demonstrateConsumer() { System.out.println("\n=== CONSUMER ==="); List<String> names = Arrays.asList("Alice", "Bob", "Carol"); Consumer<String> print = s -> System.out.println("Name: " + s); Consumer<String> logLength = s -> System.out.println("Length: " + s.length()); // Single consumer names.forEach(print); // Chaining consumers Consumer<String> printAndLog = print.andThen(logLength); System.out.println("\nChained:"); names.forEach(printAndLog); } // Supplier<T>: () -> T (provide values) static void demonstrateSupplier() { System.out.println("\n=== SUPPLIER ==="); Supplier<Double> randomSupplier = () -> Math.random(); Supplier<List<String>> listSupplier = () -> new ArrayList<>(); Supplier<String> uuidSupplier = () -> UUID.randomUUID().toString(); System.out.println("Random: " + randomSupplier.get()); System.out.println("New list: " + listSupplier.get()); System.out.println("UUID: " + uuidSupplier.get()); // Lazy initialization System.out.println("Creating expensive object..."); String value = getOrCreate(null, () -> { System.out.println(" Computing expensive value..."); return "Expensive Result"; }); System.out.println("Value: " + value); } static String getOrCreate(String cached, Supplier<String> supplier) { return cached != null ? cached : supplier.get(); } // UnaryOperator and BinaryOperator static void demonstrateOperators() { System.out.println("\n=== OPERATORS ==="); // UnaryOperator<T>: T -> T (same type) UnaryOperator<Integer> increment = n -> n + 1; UnaryOperator<String> wrapBrackets = s -> "[" + s + "]"; System.out.println("Increment 5: " + increment.apply(5)); System.out.println("Wrap 'hello': " + wrapBrackets.apply("hello")); // BinaryOperator<T>: (T, T) -> T (combine two) BinaryOperator<Integer> add = (a, b) -> a + b; BinaryOperator<Integer> max = (a, b) -> a > b ? a : b; BinaryOperator<String> concat = (a, b) -> a + b; System.out.println("5 + 3 = " + add.apply(5, 3)); System.out.println("max(5, 3) = " + max.apply(5, 3)); System.out.println("Concat: " + concat.apply("Hello", "World")); } // Advanced: Combining functions static void demonstrateCombining() { System.out.println("\n=== COMBINING ==="); List<String> words = Arrays.asList("apple", "banana", "cherry"); // Predicate: Filter long words Predicate<String> isLong = s -> s.length() > 5; // Function: Transform to uppercase Function<String, String> upper = String::toUpperCase; // Consumer: Print with prefix Consumer<String> printWithPrefix = s -> System.out.println("=> " + s); // Combine all words.stream() .filter(isLong) // Predicate .map(upper) // Function .forEach(printWithPrefix); // Consumer } } // BiFunction, BiConsumer, BiPredicate class BiFunctionalInterfacesDemo { public static void main(String[] args) { // BiFunction<T, U, R>: (T, U) -> R BiFunction<Integer, Integer, Integer> multiply = (a, b) -> a * b; System.out.println("3 * 4 = " + multiply.apply(3, 4)); // BiConsumer<T, U>: (T, U) -> void Map<String, Integer> map = new HashMap<>(); map.put("Alice", 30); map.put("Bob", 25); BiConsumer<String, Integer> printer = (k, v) -> System.out.println(k + " is " + v + " years old"); map.forEach(printer); // BiPredicate<T, U>: (T, U) -> boolean BiPredicate<String, Integer> lengthMatches = (s, n) -> s.length() == n; System.out.println("'hello' has length 5? " + lengthMatches.test("hello", 5)); } }

5. The Comparison & Decision Layer

ScenarioUse ThisExample
Test conditionPredicates -> s.isEmpty()
TransformFunctions -> s.length()
Side effectConsumers -> System.out.println(s)
Provide valueSupplier() -> new ArrayList<>()
Same type I/OUnaryOperatorn -> n * 2
Combine twoBinaryOperator(a,b) -> a + b

6. The "Interview Corner" (The Edge)

The "Killer" Interview Question: "Why use Predicate instead of just boolean lambda?" Answer: Predicate provides combinator methods (and/or/negate)!

java
// ❌ Just boolean lambda (no combinators) List<Integer> filtered = list.stream() .filter(n -> n > 0 && n % 2 == 0) // Complex inline .collect(Collectors.toList()); // ✅ Predicate with combinators (readable, reusable) Predicate<Integer> isPositive = n -> n > 0; Predicate<Integer> isEven = n -> n % 2 == 0; List<Integer> filtered = list.stream() .filter(isPositive.and(isEven)) // Clear, composable! .collect(Collectors.toList());

Pro-Tip: Primitive functional interfaces (avoid boxing):

java
// ❌ SLOW: Boxing (Integer wrapping) Predicate<Integer> isEven = n -> n % 2 == 0; // For 1M elements: Wraps 1M ints → Integers (slow!) // ✅ FAST: No boxing IntPredicate isEven = n -> n % 2 == 0; // Works directly with primitive ints (fast!) // Other primitives: IntFunction, LongConsumer, DoubleSupplier, etc.

Topics Covered

Java Fundamentals

Tags

#java#programming#beginner-friendly

Last Updated

2025-02-01