Function interface in java 8
Created on: Oct 1, 2024
Functional programming is a paradigm that allow programming using expression. We can declare, pass function as argument, statement.
Below are four main functional program introduced in java 8.
- Consumer
- Predicate
- Supplier
- Function
Let's understand each of them using example.
1. Consumer
It is a built in package in java.util.function package. This takes a input value and return nothing.
// syntex public interface Consumer<T> { void accept(T t); default Consumer<T> andThen(Consumer<? super T> after) }
class User { private String name; private String email; public User(String name, String email) { this.name = name; this.email = email; } public String getName() { return name; } public String getEmail() { return email; } } class NotificationService { public static void main(String[] args) { List<User> users = Arrays.asList( new User("John", "john@example.com"), new User("Alice", "alice@example.com"), new User("Bob", "bob@example.com") ); Consumer<User> sendNotification = new Consumer<User>() { @Override public void accept(User user) { System.out.println("Sending notification to " + user.getName() + " at " + user.getEmail()); } }; users.forEach(sendNotification); } }
Sending notification to John at john@example.com Sending notification to Alice at alice@example.com Sending notification to Bob at bob@example.com
In the above example, we can change the sendNotification part with lambda expression as follows and the result will be same.
Consumer<User> sendNotification = user -> System.out.println("Sending notification to " + user.getName() + " at " + user.getEmail());
Real usage of Consumer in collection frameworks
- forEach uses Consumer in stream api.
public interface Stream<T> extends BaseStream<T, Stream<T>> { void forEach(Consumer<? super T> action); }
forEachin map uses Biconsumer to parse the map. Below is syntex.
public interface Map<K, V> { default void forEach(BiConsumer<? super K, ? super V> action) }
public class Hello { public static void main(String[] args) { Map<String, Integer> ageMap = new HashMap<>(); ageMap.put("Alice", 25); ageMap.put("Bob", 30); ageMap.put("Charlie", 35); ageMap.forEach((key, value) -> System.out.println(key + " is " + value + " years old")); } }
2. Predicate
A Predicate is a functional interface, which accepts an argument and returns a boolean. Usually, it is used to apply in a filter for a collection of objects.
Main method in predicate functional interface.
boolean test(T value); default Predicate<T> and(Predicate<? super T> other); default Predicate<T> negate(); default Predicate<T> or(Predicate<? super T> other); static <T> Predicate<T> isEqual(Object targetRef); static <T> Predicate<T> not(Predicate<? super T> target);
Sample program to filter city
import java.util.List; import java.util.ArrayList; import java.util.function.Predicate; public class Hello { public static void main(String[] args) { List<String> cities = new ArrayList<>(); cities.add("Delhi"); cities.add("Mumbai"); cities.add("Goa"); cities.add("Pune"); Predicate<String> cityFilter = new Predicate<String>() { @Override public boolean test(String s) { return s.equalsIgnoreCase("Mumbai"); } }; String city = cities.stream().filter(cityFilter).findFirst().get(); System.out.println(city); } }
In above case we can change cityFilter in lambda expression.
Predicate<String> cityFilter = s-> s.equalsIgnoreCase("Mumbai");
3. Function
A Function is another in-build functional interface in java.util.function package, the function takes an input value and returns a value. Below are main method in Function interface.
R apply(T var1); default <V> Function<V, R> compose(Function<V, T> before); default <V> Function<T, V> andThen(Function<R, V> after); static <T> Function<T, T> identity();
Given a list of city, print the first letter of each city in a list
import java.util.List; import java.util.ArrayList; import java.util.function.Function; import java.util.stream.Collectors; public class Hello { public static void main(String[] args) { List<String> cities = new ArrayList<>(); cities.add("Delhi"); cities.add("Mumbai"); cities.add("Goa"); cities.add("Pune"); Function<String, Character> function = new Function<String, Character>() { @Override public Character apply(String s) { return s.charAt(0); } }; List<Character> characterList = cities.stream().map(function).collect(Collectors.toList()); System.out.println(characterList); } }
Supplier
It does not take any input but produce a value.
import java.util.List; import java.util.function.Supplier; import java.util.stream.Collectors; public class Hello { public static void main(String[] args) { Supplier<List<String>> supplier = new Supplier<List<String>>() { @Override public List<String> get() { return List.of("Delhi", "Mumbai", "Goa", "Pune"); } }; List<String> cities = supplier.get().stream().collect(Collectors.toList()); System.out.println(cities); } }
