Complete Java Function interface tutorial covering all methods with examples. Learn about functional programming in Java.
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 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.
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.
Main.java
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.
The andThen method allows chaining functions where the output of one becomes input to the next. This enables creating complex transformations from simple functions.
Main.java
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.
The compose method is similar to andThen but executes functions in reverse order. The parameter function runs first, then the original function.
Main.java
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.
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.
Main.java
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.
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.
Main.java
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.
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.
Main.java
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.
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.
My name is Jan Bodnar, and I am a dedicated programmer with many years of experience in the field. I began writing programming articles in 2007 and have since authored over 1,400 articles and eight e-books. With more than eight years of teaching experience, I am committed to sharing my knowledge and helping others master programming concepts.
List all Java tutorials.