The Function interface

The Funcion interface denotes a lambda expression that transforms objectos of one type into another. For example, if we define a method as follows:

public <T> void printDescriptions(List<T> items, Function<T, String> descriptionGetter) {
	...
}

We can then call our printDescriptions() method as follows, putting a lambda expression in the place of the Function declaration:

printDescriptions(customers, c -> c.getCustomerName());

In other words, we list two parameter types in our Function declaration. The first (T in this case) is the type of object that we want to transform; the second, String in this case, is the type of object that we will transform it into. This means that descriptionGetter will be a lambda expression that transforms objects of type T into strings. Or put another way, it will "define how to get a string property" from an object of type T. And in this case, T is actually Customer, and c -> c.getCustomerName() is a lambda expression that defines how to transform a Customer into a String. (We could also use a method reference: see below.)

So that's how we call our method using a lambda expression for the Function. But how do we implement the method? If you look at the definition of Function, you will see that it is defined to have an apply() method. This is the method we call to invoke the lambda expression passed in. Inside the printDescriptions() method, whenever we actually want to perform the transformation, we would invoke the lambda expression by using the Function.apply() method. Hence, our printDescriptions() might be implemented as follows:

public <T> void printDescriptions(List<T> items, Function<T, String> descriptionGetter) {
	for (int i = 0; i < items.size(); i++) {
		String desc = descipritonGetter.apply();
		System.out.println("#" + i + ": " + desc);
	}
}

Using a Function with streams and the map() method

As you might imagine, transforming one object into another is a common operation. If we are working with a Stream of objects, then we can pass a Function into the map() method to denote that we want to transform the objects in the stream from one type to another. For example, if we wanted to take a list of Customers and transform this into a list of corresponding names, we could write:

customers.stream()
  .map(c -> getCustomerName())
  .collect(Collectors.toList());

Using Function with a method reference

If the lambda expression of our function is simply going to call a method on the input parameter that returns the type that we are transforming into, then we can use a method reference. So the above example could be rewritten as follows:

printDescriptions(customers, Customer::getCustomerName);

As with method references generally, this works because the lambda arguments (T, String) match the number and types of arguments of the method being called, including the object being called on (T.getCustomerName(), taking no more arguments and returning a String). If the number or types of arguments didn't match— e.g. if getCustomerName() took an extra 'mode' parameter for example— then we would need to write in full:

printDescriptions(customers, c -> c.getCustomerName(Mode.FULL_NAME));

Tip: String.valueOf() etc are compatible with Function

Any static method that takes the object type being transformed from and returns the type being transformed to is also compatible with Function. Therefore, we could call printDescriptions on a list of Integers as follows:

printDescriptions(ints, String::valueOf);

Similarly, we can pass String::valueOf into map() and transform a list of Integers into a list of strings as follows:

List<String> strs = ints.stream()
  .map(String::valueOf)
  .collect(Collectors.toList());

or produce a comma-separated list of the integers as follows:

List<String> commaSep = ints.stream()
  .map(String::valueOf)
  .collect(Collectors.joining(","));

If you enjoy this Java programming article, please share with friends and colleagues. Follow the author on Twitter for the latest news and rants.

Editorial page content written by Neil Coffey. Copyright © Javamex UK 2021. All rights reserved.