ZetCode

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.

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.

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.

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.

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.

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.

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.

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.

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.

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.

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.

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.

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

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.