skip to content

Functional Interfaces

What makes an interface functional, the built-in java.util.function catalog, and the default methods that let you compose them. Knowing the catalog by name is the difference between writing streams fluently and fighting them.

part ofJavaoverview, primer and where to startread it →
on this pageshow

questions

14

What are the four core functional interfaces in java.util.function, and what is the shape (inputs and output) of each?

level: juniorimportance: must knowfreq 80%

answer

  1. Function = apply, Consumer = accept, Supplier = get, Predicate = test
  2. Shape = inputs + output: 1→value, 1→void, 0→value, 1→boolean
  3. Predicate returns primitive boolean, not Function<T,Boolean>
  4. map=Function, filter=Predicate, forEach=Consumer, orElseGet=Supplier
  5. All are single-abstract-method interfaces (SAM)

basics

~10 s

Function<T,R> takes one value and returns another. Consumer<T> takes one value, returns nothing. Supplier<T> takes nothing, returns a value. Predicate<T> takes one value, returns a boolean.

solid answer

~40 s

The java.util.function package defines four core single-argument interfaces, each a target type for a lambda. Function<T,R> maps an input of type T to an output of type R via apply. Consumer<T> accepts a T and returns nothing (void) via accept — it works by side effect, like printing. Supplier<T> takes no input and produces a T via get, used for lazy or deferred values. Predicate<T> takes a T and returns a boolean via test, used for filtering and conditions. Knowing the shape — how many inputs and whether there is a return — lets you pick the right one: 'transforms one thing into another' is Function, 'consumes' is Consumer, 'produces from nothing' is Supplier, 'tests' is Predicate. They are what stream operations like map, filter, and forEach expect.

code

java · 9 lines
java
Function<String, Integer> length = String::length;   // T -> R
Consumer<String> printer = System.out::println;       // T -> void
Supplier<Double> random = Math::random;               // () -> T
Predicate<String> isEmpty = String::isEmpty;          // T -> boolean

System.out.println(length.apply("hello"));  // 5
printer.accept("side effect only");          // prints, returns nothing
System.out.println(random.get());            // produces a value
System.out.println(isEmpty.test(""));        // true

go deeper

for a junior

Can name the four interfaces and state each one's shape (how many inputs, what it returns) and its single method name.

for a middle

Maps each interface to the stream operation that consumes it (map/filter/forEach) and explains why Predicate is a distinct type from Function<T,Boolean>.

for a senior

Explains the single-abstract-method foundation, lazy evaluation via Supplier (orElseGet vs orElse), and when reusing the core four is cleaner than declaring a custom interface.

for a principal

Frames the catalog as a deliberate API design — minimal core types plus systematic variations — and weighs the readability/boxing trade-offs that justify purpose-built interfaces over a single generic Function everywhere.

## What a functional interface is A **functional interface** is an interface with exactly **one abstract method** (the 'single abstract method', or SAM). Because there is only one method to implement, the Java compiler lets you supply it as a **lambda expression** (`x -> x + 1`) or a **method reference** (`String::length`) instead of writing a full anonymous class. The lambda's parameters and body must match that one abstract method's signature. Java ships a ready-made catalog of these interfaces in the package **`java.util.function`** so you rarely have to declare your own. Four of them are the foundation; everything else in the package is a variation on these. ## The four core interfaces Think of each by its **shape**: *how many inputs* and *whether it returns something*. | Interface | Abstract method | Inputs | Output | Mnemonic | |---|---|---|---|---| | `Function<T,R>` | `R apply(T t)` | one (T) | a value (R) | transforms T into R | | `Consumer<T>` | `void accept(T t)` | one (T) | nothing (void) | consumes/uses a T | | `Supplier<T>` | `T get()` | none | a value (T) | supplies a T from nothing | | `Predicate<T>` | `boolean test(T t)` | one (T) | a boolean | tests a T | - **`Function<T,R>`** — a mapping. `T` is the input type, `R` the result type (they can be the same or different). Example: `Function<String,Integer> len = String::length;` then `len.apply("hi")` is `2`. - **`Consumer<T>`** — does something with its argument and returns nothing. It is useful only for its **side effect** (printing, storing, logging). Example: `Consumer<String> print = System.out::println;` then `print.accept("hi")` prints `hi`. - **`Supplier<T>`** — a factory or deferred value. It takes no argument and yields a `T` each time you call `get()`. Useful for **laziness**: the work happens only when `get()` runs. Example: `Supplier<Double> rnd = Math::random;`. - **`Predicate<T>`** — a boolean-valued test. Example: `Predicate<String> empty = String::isEmpty;` then `empty.test("")` is `true`. Predicates drive filtering. ## Why a separate boolean type A predicate is technically just a `Function<T,Boolean>`, but `Predicate<T>` returns the **primitive** `boolean` (not the boxed `Boolean`), avoiding an object allocation, and it adds combinator methods (`and`, `or`, `negate`). That is the recurring theme of this package: purpose-built types are clearer and often faster than reusing `Function` for everything. ## Where you meet them The Stream API and `Optional` are built on these types: `stream.map(fn)` takes a `Function`, `stream.filter(p)` takes a `Predicate`, `stream.forEach(c)` takes a `Consumer`, and `Optional.orElseGet(s)` takes a `Supplier`. So recognizing the four shapes is really recognizing the vocabulary of functional Java. ## Deriving an answer at any level Given a lambda, ask: *Does it take an argument? Does it return a value?* No argument + returns → `Supplier`. Argument + returns boolean → `Predicate`. Argument + returns another value → `Function`. Argument + returns nothing → `Consumer`. That decision tree reproduces the whole core catalog.

  • Why does Predicate<T> exist when Function<T,Boolean> could do the same job?
    Predicate returns a primitive boolean (no Boolean boxing) and adds boolean combinators (and/or/negate). It is clearer and slightly cheaper, and it is the type the stream filter and Optional.filter operations expect.
  • Which functional interface would you pass to Optional.orElseGet, and why not orElse?
    A Supplier<T>. orElseGet defers the work — the supplier's get() runs only if the Optional is empty — whereas orElse always evaluates its argument eagerly, even when the value is present.

saying these in an interview costs you the question

  • Saying Consumer returns a value — it returns void; it works by side effect
  • Confusing the method names (calling Predicate's method 'apply' instead of 'test')
  • Thinking Supplier takes an argument — it takes none
  • Claiming you must write an anonymous class — a lambda or method reference is the point

context

open as a page

In Java, what is the difference between Function.andThen and Function.compose? Given f.andThen(g) versus f.compose(g), which function runs first?

level: juniorimportance: must knowfreq 70%

basics

~10 s

Both glue two functions into one. f.andThen(g) runs f first, then feeds its result to g. f.compose(g) runs g first, then feeds its result to f. So andThen reads left-to-right; compose reads right-to-left.

open as a page

How do you combine Predicates in Java using and, or, and negate, and what do these combinators return?

level: juniorimportance: must knowfreq 65%

basics

~10 s

Predicate is a function returning true/false. p.and(q) is true only if both are true; p.or(q) is true if either is true; p.negate() flips the result. Each returns a new Predicate you can keep combining.

open as a page

What is a functional interface in Java, and what does the SAM rule require?

level: juniorimportance: must knowfreq 78%

basics

~20 s

A functional interface is an interface with exactly one abstract method (the SAM rule, Single Abstract Method). Because there is only one method to implement, you can supply it with a lambda or method reference instead of a full class.

open as a page

Beyond the four core interfaces, what are the arity-2 variants (BiFunction, BiConsumer, BiPredicate) and the operator specializations (UnaryOperator, BinaryOperator)? How do they relate to the core types?

level: middleimportance: should knowfreq 62%

basics

~20 s

The Bi- versions take two arguments instead of one: BiFunction<T,U,R>, BiConsumer<T,U>, BiPredicate<T,U>. UnaryOperator<T> is a Function whose input and output are the same type; BinaryOperator<T> is a BiFunction with both inputs and output the same type.

open as a page

Several built-in functional interfaces provide default methods like andThen, compose, and the Predicate combinators and/or/negate. What do these do, and how does composition order differ between Function.andThen and Function.compose?

level: middleimportance: should knowfreq 55%

basics

~20 s

These default methods let you combine small functions into bigger ones without writing glue code. Function.andThen(g) runs this first then g; Function.compose(g) runs g first then this. Predicate.and/or/negate combine boolean tests; Consumer.andThen runs two consumers in sequence.

open as a page

What does Consumer.andThen do, and how does composing Consumers differ from composing Functions?

level: middleimportance: should knowfreq 45%

basics

~20 s

Consumer takes a value and returns nothing (it does a side effect). c1.andThen(c2) makes one Consumer that runs c1 then c2 on the SAME input — it doesn't pass a result along, because Consumers produce no result.

open as a page

What does the @FunctionalInterface annotation do, and what happens if you apply it to an interface with two abstract methods?

level: middleimportance: should knowfreq 62%

basics

~20 s

@FunctionalInterface tells the compiler to check that the interface has exactly one abstract method. If you put it on an interface with two abstract methods, the code fails to compile. It is optional and only adds this check.

open as a page

Why does java.util.function include primitive specializations like IntFunction, ToIntFunction, and IntPredicate? What problem do they solve and how do you read their naming convention?

level: seniorimportance: should knowfreq 58%

basics

~20 s

Generics can't use primitives, so a plain Function<Integer,…> would box every int into an Integer object. The Int/Long/Double specializations work directly with primitives to avoid that boxing cost. The name tells you where the primitive sits: IntFunction takes an int, ToIntFunction returns an int.

open as a page

Why are andThen, compose, and and/or/negate implemented as default methods on the functional interfaces, and what does that imply for lambdas and custom implementations?

level: seniorimportance: should knowfreq 35%

basics

~10 s

They're default methods — concrete methods written on the interface itself. That keeps the single abstract method intact (so lambdas still work) while giving every Function/Predicate/Consumer ready-made combinators for free, without an extra class.

open as a page

Why can an interface re-declare a public method of Object (like equals) and still qualify as a functional interface?

level: seniorimportance: should knowfreq 45%

basics

~20 s

Every implementation of an interface is an Object and already inherits concrete equals, hashCode, and toString. So a re-declared Object method is always already implemented and doesn't need a lambda body, which is why it doesn't count as the interface's single abstract method.

open as a page

Given the rich java.util.function catalog, when is it still justified to declare your own functional interface instead of reusing a built-in one? What does @FunctionalInterface add?

level: principalimportance: nice to knowfreq 40%

basics

~20 s

Reuse a built-in interface when its shape fits. Declare your own when you need a name that conveys domain meaning, more than two parameters, checked exceptions in the signature, or extra default methods. @FunctionalInterface is a compile-time check that the interface has exactly one abstract method.

open as a page

When building a transformation pipeline, when would you compose Function objects with andThen/compose versus chaining Stream.map calls, and what trade-offs guide the choice?

level: principalimportance: nice to knowfreq 25%

basics

~20 s

Use Function composition (andThen/compose) to build a single reusable transform you can name, pass around, test, and reuse in many places. Use Stream.map chaining when the steps are inline stages of a one-off data flow. Both produce the same result; one is a reusable value, the other is in-place pipeline structure.

open as a page

Can an interface with a generic method, or one inheriting abstract methods from two parents, be a functional interface? How is the single-abstract-method count actually computed?

level: principalimportance: nice to knowfreq 28%

basics

~20 s

Yes, a generic method can be the single abstract method. The count is over distinct method signatures after merging override-equivalent ones from all parents. If two unrelated abstract methods survive, the interface is not functional; if inherited methods are override-equivalent they collapse to one and it still is.

open as a page