Java Function Functional Interface: apply(), Transformation, Composition & Examples

0

Function

A Function is a functional interface in Java that represents an operation which accepts one input value and produces one output value. It is especially useful when you want to transform data from one form into another.

Think of a Function as a small transformation machine: you give it something, it processes that value, and it gives you a result. Converting a name to uppercase, calculating a price with tax, extracting a student's marks, or converting an object into a string are all examples of functional transformations.

What Is Function?

The Function<T, R> interface belongs to the java.util.function package.

@FunctionalInterface
public interface Function<T, R> {
    R apply(T t);
}

The two generic types are important. T represents the input type, while R represents the result type.

Remember: Function takes one input and produces one output. Its primary method is apply().

Basic Function Example

import java.util.function.Function;

public class Main {
    public static void main(String[] args) {
        Function<String, Integer> length =
            text -> text.length();

        System.out.println(length.apply("Java"));
    }
}

The Function accepts a String and returns an Integer. The input type is String, while the output type is Integer.

Understanding Function<T, R>

Generic Type Meaning
T Type of input value
R Type of returned result

For example, Function<String, Integer> means the Function accepts a String and returns an Integer.

Function with the Same Input and Output Type

The input and output types do not have to be different. A Function can also accept and return the same type.

Function<String, String> upperCase =
    text -> text.toUpperCase();

System.out.println(
    upperCase.apply("java programming")
);

The Function receives a String and returns another String containing the transformed value.

Function with Numbers

Function<Integer, Integer> square =
    number -> number * number;

System.out.println(square.apply(8));

The input and output are both Integer values, but the Function changes the input by calculating its square.

Function with Different Types

One of the biggest advantages of Function is that the input and output can have completely different types.

Function<String, Integer> convert =
    text -> Integer.parseInt(text);

System.out.println(convert.apply("500"));

The string "500" enters the Function and an Integer value 500 comes out.

Function with Custom Objects

Functions are very useful when extracting information from domain objects.

import java.util.function.Function;

class Student {
    String name;
    int marks;

    Student(String name, int marks) {
        this.name = name;
        this.marks = marks;
    }
}

public class Main {
    public static void main(String[] args) {
        Function<Student, Integer> getMarks =
            student -> student.marks;

        Student student =
            new Student("Ravi", 85);

        System.out.println(
            getMarks.apply(student)
        );
    }
}

The Function receives a Student object and extracts only its marks. This is a common pattern when working with collections and streams.

Function as a Method Parameter

A method can accept a Function as an argument, allowing the caller to decide how the input should be transformed.

import java.util.function.Function;

public class Main {

    static void process(
            String value,
            Function<String, String> transformer) {

        String result = transformer.apply(value);
        System.out.println(result);
    }

    public static void main(String[] args) {
        process(
            "java",
            text -> text.toUpperCase()
        );
    }
}

The method is not tied to one particular transformation. A different Function can be supplied whenever different behavior is required.

Function with Collections

Function becomes especially powerful with Java Streams. The map() operation commonly accepts a Function to transform every element.

import java.util.*;

public class Main {
    public static void main(String[] args) {
        List<String> names = Arrays.asList(
            "Ravi",
            "Anita",
            "Priya"
        );

        names.stream()
             .map(name -> name.toUpperCase())
             .forEach(System.out::println);
    }
}

The map() operation applies the Function to every element and produces a new stream containing the transformed values.

Storing a Function in a Variable

Function<Integer, Integer> doubleValue =
    number -> number * 2;

System.out.println(doubleValue.apply(10));
System.out.println(doubleValue.apply(25));

The same transformation can now be reused with different input values.

Function Composition

One of the most powerful features of Function is composition. You can connect multiple Functions so that the output of one becomes the input of another.

Java provides three important methods for this purpose: andThen(), compose(), and identity().

Function andThen()

The andThen() method executes the current Function first and then passes its result to another Function.

Function<Integer, Integer> doubleValue =
    number -> number * 2;

Function<Integer, Integer> addTen =
    number -> number + 10;

Function<Integer, Integer> result =
    doubleValue.andThen(addTen);

System.out.println(result.apply(5));

For the input 5, the first Function produces 10. The second Function then adds 10, producing 20.

Function compose()

The compose() method reverses the execution order compared with andThen(). The supplied Function executes first.

Function<Integer, Integer> doubleValue =
    number -> number * 2;

Function<Integer, Integer> addTen =
    number -> number + 10;

Function<Integer, Integer> result =
    doubleValue.compose(addTen);

System.out.println(result.apply(5));

Here, addTen executes first: 5 becomes 15. Then doubleValue executes: 15 becomes 30.

Method Execution Order
andThen() Current Function → supplied Function
compose() Supplied Function → current Function

Function identity()

The static identity() method returns a Function that simply returns its input unchanged.

Function<String, String> identity =
    Function.identity();

System.out.println(
    identity.apply("Java")
);

The input string is returned exactly as it was received.

Practical Example: Price Calculation

Function<Double, Double> addTax =
    price -> price * 1.18;

double finalPrice = addTax.apply(1000.0);

System.out.println(finalPrice);

The Function receives the original price and returns the price after applying an 18 percent calculation. This keeps the transformation logic separate from the code that uses it.

Practical Example: Student Grade

Function<Integer, String> grade =
    marks -> {
        if (marks >= 90) {
            return "A";
        } else if (marks >= 75) {
            return "B";
        } else if (marks >= 60) {
            return "C";
        } else {
            return "D";
        }
    };

System.out.println(grade.apply(82));

Here, an Integer is transformed into a String. This is exactly the kind of input-to-output conversion for which Function is designed.

Function vs Predicate

The distinction becomes easy once you focus on the result. Predicate always returns a boolean, while Function can return any type.

Feature Predicate Function
Input One value One value
Output boolean Any type
Method test() apply()
Purpose Check a condition Transform a value

Function vs Consumer

Feature Function Consumer
Input One value One value
Output One value None
Method apply() accept()
Purpose Transform data Perform an action

Function vs Supplier

Feature Function Supplier
Input One value None
Output One value One value
Method apply() get()
Purpose Transform an input Produce a value

Common Beginner Mistakes

  • Using test() instead of apply().
  • Forgetting that Function<T, R> has separate input and output types.
  • Using Function when the operation only needs to check a condition; Predicate is more appropriate in that case.
  • Using Function for an operation that produces no result; Consumer is designed for that purpose.
  • Confusing the execution order of andThen() and compose().

Best Practices

  • Use Function when the core operation is input-to-output transformation.
  • Give reusable Functions descriptive names.
  • Use small Functions that perform one clear transformation.
  • Use andThen() and compose() when combining transformations improves readability.
  • Prefer method references when they make simple transformations clearer.

Interview Insight

Interview shortcut: Function<T, R> accepts one argument of type T and returns a result of type R through apply(). It is commonly used for transformation, mapping, and function composition.

Quick Revision

Concept Key Point
Function<T, R> Transforms one input into one output
apply() Executes the Function
T Input type
R Output type
andThen() Executes the current Function before another Function
compose() Executes another Function before the current Function
identity() Returns the input unchanged

Final Takeaway

Function is the functional interface to choose when one value needs to be transformed into another. Its apply() method makes it ideal for data conversion, extraction, mapping, and reusable business transformations. Once you understand the pattern "one input goes in, one transformed result comes out," Function becomes a powerful building block for Java lambdas, streams, and functional programming.

Post a Comment

0Comments
Post a Comment (0)