Primitive Functional Interfaces in Java

Naveen AnamaneniUpdated · 4 min read

Primitive functional interfaces in Java are versions of the built-in functional interfaces that work directly with int, long and double instead of their wrapper classes Integer, Long and Double. They are used to avoid boxing: turning a primitive into an object and back, which costs time and memory when it happens millions of times. For example, IntPredicate tests an int without creating an Integer. In this lesson, you will learn the naming pattern of these interfaces and see them in use.

Why They Exist

Generics only work with objects, so Predicate<Integer> must box every int into an Integer before calling test. In a loop over a million numbers, that is a million small objects. IntPredicate has boolean test(int value), so no object is created.

The Naming Pattern

PatternMeaningExamples
IntXxx, LongXxx, DoubleXxxThe input is a primitiveIntPredicate, IntConsumer, IntFunction<R>
ToIntXxx, ToLongXxx, ToDoubleXxxThe output is a primitiveToIntFunction<T>, ToDoubleBiFunction<T, U>
IntToLongFunction, DoubleToIntFunction...Primitive in and outIntToDoubleFunction
XxxSupplierSupplies a primitiveIntSupplier, BooleanSupplier
XxxUnaryOperator, XxxBinaryOperatorSame primitive in and outIntUnaryOperator, IntBinaryOperator
ObjIntConsumer<T>An object and an intObjIntConsumer<String>

The Most Used Ones

InterfaceMethodDescription
IntPredicateboolean test(int v)It tests an int.
IntFunction<R>R apply(int v)It turns an int into an object.
ToIntFunction<T>int applyAsInt(T t)It turns an object into an int.
IntUnaryOperatorint applyAsInt(int v)It changes an int into another int.
IntBinaryOperatorint applyAsInt(int a, int b)It combines two int values.
IntConsumervoid accept(int v)It uses an int.
IntSupplierint getAsInt()It supplies an int.

Example: Primitive Interfaces in Action

Let's see an example that uses several of them.

Primitives.java▶ Try it
import java.util.function.IntBinaryOperator;
import java.util.function.IntFunction;
import java.util.function.IntPredicate;
import java.util.function.IntUnaryOperator;
import java.util.function.ToIntFunction;
 
public class Primitives {
    public static void main(String[] args) {
        IntPredicate isEven = n -> n % 2 == 0;
        IntUnaryOperator square = n -> n * n;
        IntBinaryOperator max = Math::max;
        IntFunction<String> stars = n -> "*".repeat(n);
        ToIntFunction<String> length = String::length;
 
        System.out.println("8 even? " + isEven.test(8));
        System.out.println("square(7) = " + square.applyAsInt(7));
        System.out.println("max(91, 78) = " + max.applyAsInt(91, 78));
        System.out.println("stars(5) = " + stars.apply(5));
        System.out.println("length(Navika) = " + length.applyAsInt("Navika"));
    }
}
Output
8 even? true
square(7) = 49
max(91, 78) = 91
stars(5) = *****
length(Navika) = 6

Explanation: Each interface works with plain int values, so no Integer objects are created. Note the method names: interfaces that return a primitive use names such as applyAsInt and getAsInt, so it's clear what type comes back.

Example: They Power Primitive Streams

IntStreams.java▶ Try it
import java.util.List;
import java.util.stream.IntStream;
 
public class IntStreams {
    public static void main(String[] args) {
        int sumOfSquares = IntStream.rangeClosed(1, 5)
            .filter(n -> n % 2 == 1)
            .map(n -> n * n)
            .sum();
        System.out.println("1 + 9 + 25 = " + sumOfSquares);
 
        int letters = List.of("Navika", "Srihitha", "Advitha").stream()
            .mapToInt(String::length)
            .sum();
        System.out.println("Total letters: " + letters);
    }
}
Output
1 + 9 + 25 = 35
Total letters: 21

Explanation: IntStream.filter takes an IntPredicate, map takes an IntUnaryOperator, and mapToInt takes a ToIntFunction. The numbers stay as int from start to finish. Primitive streams are covered in "Primitive Streams in Java".

Common Mistakes

Calling apply on an IntUnaryOperator. Its method is applyAsInt.

Expecting IntFunction<R> to return an int. It takes an int; ToIntFunction<T> returns one.

Looking for CharPredicate or FloatFunction. Only int, long, double (and boolean for suppliers) have versions; use int for char.

Practice

  1. Easy: Write an IntPredicate that checks whether a number is between 1 and 100, and test it.
  2. Harder: Use IntStream.rangeClosed(1, 20) with an IntPredicate for prime numbers and print them.

Quick check: Primitive functional interfaces

4 questions
1Why do primitive functional interfaces exist?
2What does ToIntFunction do?
3What is IntUnaryOperator's method?
4Which interface does IntStream.filter take?

Key Takeaways

Was this tutorial helpful?

Lesson 308 of 482