Explain Codes LogoExplain Codes Logo

How to specify function types for void (not Void) methods in Java8?

java
functional-programming
java-8
best-practices
Nikita BarsukovbyNikita Barsukov·Jan 1, 2025
TLDR

Pocket-sized solution: Use Consumer<T> for methods with a single parameter and void return type:

Consumer<String> echo = System.out::println; // Cheap echo, echo.accept("Echo");

For void methods without parameters, Runnable is your friend:

Runnable show = () -> System.out.println("Displayed"); // Run, Forrest, Run! show.run();

And for methods with two parameters and void return, utilize BiConsumer<T, U>:

BiConsumer<Integer, Integer> addAndPrint = (x, y) -> System.out.println(x + y); // Simple calculator, addAndPrint.accept(1, 2);

Making sense of Functional Interfaces

Single-parameter void methods: Consumer<T>

For functions that do their job silently (a.k.a. return void) and take one parameter, Consumer<T> works wonders. The function fits snugly inside the accept(T t) method:

Consumer<Double> squareRootPrinter = Math::sqrt; // Math whiz in action, squareRootPrinter.accept(4.0)

Two-parameter void methods and beyond: BiConsumer and friends

When a function takes two witnesses but remains silent (void), BiConsumer<T, U> handles the business. If there are more than two confidants, custom functional interfaces or object encapsulation come to the rescue.

Parameter-less void scenarios: Runnable

Run Runnable run! The workhorse interface of Java, known for its use in threading, is also a perfect match for parameter-less void-return functions:

Runnable greet = () -> System.out.println("Hello, World!"); // This function has greeting disorder. greet.run();

When generic doesn't cut it: Custom Functional Interfaces

Custom-made solution for you: custom functional interfaces. It's like naming your high-functioning pet rock:

@FunctionalInterface interface Thunk { void apply(); } Thunk petRock = () -> {}; // It's a lazy pet rock that does nothing. Yet it's special.

Critically, they can make your intent clear and code flexible.

Making void cool again with Method References

Nobody in Java land writes Lambdas longer than they absolutely need to. Enter method references. When the referenced method is void, it's Consumer time again:

Consumer<List<String>> sort = Collections::sort; // It's a kind of magic. sort.accept(list); // Your list now knows its place.

Void’s compatibility patch: Null

Those pesky APIs expecting Function<T, Void> can be accommodated with null:

Function<String, Void> logger = s -> { System.out.println(s); return null; }; // Talkative logger. Always ends in silence.

Naming interfaces: The art of clarity

A well-named functional interface is like a well-marked road. Choose names to illuminate the direction of your code:

@FunctionalInterface interface Action { void perform(); } Action saveAction = () -> {/* The hero code that saves the world, quietly. */};