Java Function Interface
Last modified: April 16, 2025
The java.util.function.Function
interface represents a function
that accepts one argument and produces a result. It is a functional interface
with a single abstract method apply
. Function is commonly used for
transforming data in stream operations and method references.
Function
is part of Java's functional programming utilities added
in Java 8. It enables behavior parameterization and helps write more concise
code. The interface provides default methods for function composition and
chaining.
Function Interface Overview
Function
interface contains one abstract method and several default
methods. The key method apply
performs the operation on the input.
Other methods enable function composition and transformation chaining.
@FunctionalInterface public interface Function<T, R> { R apply(T t); default <V> Function<V, R> compose(Function<? super V, ? extends T> before); default <V> Function<T, V> andThen(Function<? super R, ? extends V> after); static <T> Function<T, T> identity(); }
The code above shows the structure of Function
interface. It uses
generics where T is input type and R is result type. The interface is annotated
with @FunctionalInterface to indicate its single abstract method nature.
Basic Function Usage
The simplest way to use Function is with lambda expressions. We define how to transform input to output in the apply method. The example converts strings to their lengths.
package com.zetcode; import java.util.function.Function; public class Main { public static void main(String[] args) { // Define a function that takes String and returns its length Function<String, Integer> lengthFunction = s -> s.length(); // Apply the function System.out.println("Length of 'hello': " + lengthFunction.apply("hello")); System.out.println("Length of 'functional': " + lengthFunction.apply("functional")); // Function using method reference Function<String, Integer> lengthMethodRef = String::length; System.out.println("Length via method ref: " + lengthMethodRef.apply("method")); } }
This example demonstrates basic Function usage with lambda and method reference. The lengthFunction takes String and returns Integer. We apply it to different strings. Method reference provides more concise syntax for existing methods.
Function Composition with andThen
The andThen
method allows chaining functions where the output of
one becomes input to the next. This enables creating complex transformations
from simple functions.
package com.zetcode; import java.util.function.Function; public class Main { public static void main(String[] args) { // First function converts String to uppercase Function<String, String> toUpper = s -> s.toUpperCase(); // Second function extracts first 3 characters Function<String, String> firstThree = s -> s.substring(0, Math.min(s.length(), 3)); // Compose the functions Function<String, String> upperThenTrim = toUpper.andThen(firstThree); System.out.println("Result: " + upperThenTrim.apply("hello world")); System.out.println("Result: " + upperThenTrim.apply("java")); } }
This example shows function composition with andThen
. The input
string first gets converted to uppercase, then trimmed to first 3 characters.
The order of operations is left-to-right in the chain.
Function Composition with compose
The compose
method is similar to andThen
but executes
functions in reverse order. The parameter function runs first, then the original
function.
package com.zetcode; import java.util.function.Function; public class Main { public static void main(String[] args) { // Function to double a number Function<Integer, Integer> doubler = x -> x * 2; // Function to increment by 1 Function<Integer, Integer> incrementer = x -> x + 1; // Compose in different orders Function<Integer, Integer> incrementThenDouble = doubler.compose(incrementer); Function<Integer, Integer> doubleThenIncrement = doubler.andThen(incrementer); System.out.println("Increment then double 5: " + incrementThenDouble.apply(5)); System.out.println("Double then increment 5: " + doubleThenIncrement.apply(5)); } }
This example demonstrates the difference between compose
and
andThen
. With compose, the increment happens before doubling.
With andThen, doubling happens before incrementing. The results differ.
Using Function with Streams
Function is commonly used with Java Streams for data transformation. The map operation accepts a Function to transform stream elements. This enables clean data processing pipelines.
package com.zetcode; import java.util.Arrays; import java.util.List; import java.util.function.Function; import java.util.stream.Collectors; public class Main { public static void main(String[] args) { List<String> names = Arrays.asList("alice", "bob", "charlie", "dave"); // Function to capitalize first letter Function<String, String> capitalize = s -> s.substring(0, 1).toUpperCase() + s.substring(1); // Apply function in stream List<String> capitalizedNames = names.stream() .map(capitalize) .collect(Collectors.toList()); System.out.println("Original: " + names); System.out.println("Transformed: " + capitalizedNames); } }
This example shows Function usage in Streams. We define a capitalization function and apply it to each stream element via map. The result is a new list with transformed values. Stream operations become very expressive.
Function Identity
The Function.identity
method returns a function that always
returns its input argument. It's useful when an operation requires a function
but you want to pass values unchanged.
package com.zetcode; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; public class Main { public static void main(String[] args) { // Identity function Function<String, String> identity = Function.identity(); System.out.println("Identity applied: " + identity.apply("test")); // Practical use in streams Stream<String> words = Stream.of("a", "b", "c"); var result = words.collect(Collectors.toMap( Function.identity(), // key mapper String::length // value mapper )); System.out.println("Resulting map: " + result); } }
This example demonstrates Function.identity
. The identity
function returns its input unchanged. In streams, it's often used as a
placeholder when transformation isn't needed but a Function is required.
BiFunction and Beyond
While Function takes one argument, Java provides related interfaces for
different arities. BiFunction
takes two arguments, and specialized
interfaces exist for primitive types to avoid boxing.
package com.zetcode; import java.util.function.BiFunction; import java.util.function.DoubleFunction; import java.util.function.ToIntFunction; public class Main { public static void main(String[] args) { // BiFunction example BiFunction<Integer, Integer, String> sumToString = (a, b) -> String.valueOf(a + b); System.out.println("Sum as string: " + sumToString.apply(5, 3)); // Primitive specialized functions DoubleFunction<String> doubleFormatter = d -> String.format("$%.2f", d); System.out.println("Formatted: " + doubleFormatter.apply(12.3456)); ToIntFunction<String> stringToLength = String::length; System.out.println("Length as int: " + stringToLength.applyAsInt("hello")); } }
This example shows Function
variants. BiFunction
handles two inputs, while primitive specializations like
DoubleFunction
improve performance. Java's functional interfaces
cover many common use cases.
Source
Java Function Interface Documentation
In this article, we've covered the essential methods and features of the Java Function interface. Understanding these concepts is crucial for functional programming and stream processing in modern Java applications.
Author
List all Java tutorials.