Introduction
We will learn about the Google Guava Functions utility class in this post. The Functions class has static utility methods to work on Function instances.
Google Guava Function vs the Java Function
The Google Guava’s Function interface extends the Java function (java.util.function.Function). This is similar to the other Google Guava’s functional interfaces, like supplier and predicate. Hence, we can use a Google Guava Function as a Java Function functional interface.
What is a Function
I have a separate post on Function where I’ve covered it in depth. A short summary is that a Function is a functional interface that takes one argument and returns a result (which may or may not be of the same type).
Functions identity and constant
Functions#identity
The identity() method returns a Function which acts as an identity function, i.e., it returns the same object passed as input.
Function<String, String> identityFunction = Functions.identity();
System.out.println(identityFunction.apply("value")); //value
System.out.println(identityFunction.apply("")); //"" (empty string)
System.out.println(identityFunction.apply(null)); //null
The constant() method takes a target value and returns a function which always returns that target value as the result. Hence, it always ignores the passed input. The lambda expression equivalent of this would be obj -> value. It returns a function of type Function<Object, E> where E is the type of the target value we pass.
As shown below, irrespective of the input, it always returns the same result back.
Function<Object, String> constantFunction = Functions.constant("value");
System.out.println(constantFunction.apply("input")); //value
System.out.println(constantFunction.apply("some-input")); //value
System.out.println(constantFunction.apply("")); //value
Functions - forMap
There are two overloaded forMap methods.
forMap - map lookup
The Google Guava Functions forMap method takes a Map<K, V> and returns a function which derives the result by looking up into the passed map. In other words, it looks up into the map with the passed input and returns the retrieved value back.
Its method signature is
public static <K, V> Function<K, V> forMap(Map<K, V> map)
Function<String, Integer> function = Functions.forMap(
Map.of(
"key1", 1,
"key2", 2));
System.out.println(function.apply("key1")); //1
System.out.println(function.apply("key2")); //2
In the above example, I’ve used the Map.of factory method added in Java 9. It looks up the map for the passed input and it gets the value mapped to that string in the map.
If the map doesn’t have any mapping for the passed key, then it will throw an IllegalArgumentException.
// throws java.lang.IllegalArgumentException: Key 'key3' not present in map
System.out.println(function.apply("key3"));
forMap with default value
The another overloaded forMap method allows us to pass a map and a default value. If there is no mapping in the map for an input, then it will return this default value. Its method signature is:
public static <K, V> Function<K, V> forMap(Map<K, ? extends V> map, V defaultValue)
In the below example, we pass a default value of 0. Hence, when we call the apply on the returned function for “key3”, we get back the default value of 0.
Function<String, Integer> function = Functions.forMap(
Map.of(
"key1", 1,
"key2", 2),
0);
System.out.println(function.apply("key1")); //1
System.out.println(function.apply("key2")); //2
System.out.println(function.apply("key3")); //0
Functions - forSupplier
The forSupplier takes a Supplier and returns a function which ignores the input and always returns the result of invoking the supplier (i.e., supplier.get()).
Note: The supplier it takes is a Google Guava supplier and hence we cannot pass the Java supplier type. The function it returns is of type Function<Object, T> where T is the type of the data the supplier returns.
// Supplier is com.google.common.base.Supplier
Supplier<String> supplier = () -> "value";
Function<Object, String> function = Functions.forSupplier(supplier);
System.out.println(function.apply("a")); //value
System.out.println(function.apply("b")); //value
Note that it doesn’t do any caching of the supplier returned data. It calls the supplier each time.
Supplier<String> supplier = () -> {
System.out.println("Supplier called");
return "value";
};
Function<Object, String> function = Functions.forSupplier(supplier);
System.out.println(function.apply("a"));
System.out.println(function.apply("b"));
Prints,
Supplier called
value
Supplier called
value
If we want to cache, then we can use the memoize method as explained on the Suppliers utility class.
The lambda equivalent of calling forSupplier is obj-> supplier.get()
.
Functions - forPredicate
This is similar to the forSupplier method. It takes a Google Guava predicate and returns a function which returns the boolean result of calling the predicate with the passed input. It just delegates to the predicate and passes back the boolean result from it. The returned function type is Function<T, Boolean>.
// Predicate is com.google.common.base.Predicate
Predicate<Integer> isEven = i -> i % 2 == 0;
Function<Integer, Boolean> isEvenFunction = Functions.forPredicate(isEven);
System.out.println(isEvenFunction.apply(2)); //true
System.out.println(isEvenFunction.apply(3)); //false
The lambda equivalent of calling forPredicate is obj-> predicate.test(obj)
or when using method references, it is predicate::test.
Functions - compose
The compose method takes two functions and returns a function which composes them. If f and g are two passed functions, the returned function is same as g(f(a)) where a is the input. In Java 8, this is same as f.andThen(g).
Its method signature is:
public static <A, B, C> Function<A, C> compose(Function<B, C> g, Function<A, ? extends B> f)
Example: We have two functions - the first takes a string and returns its length and the second to check if an integer is even or odd. Composing them we get Function<String, Boolean> which returns true when we pass a string of even length and false otherwise.
// Function is com.google.common.base.Function
Function<String, Integer> lengthFunction = String::length; //s -> s.length();
Function<Integer, Boolean> isEvenFunction = i -> i % 2 == 0;
Function<String, Boolean> composedFunction = Functions.compose(isEvenFunction, lengthFunction);
System.out.println(composedFunction.apply("abcd")); //true
System.out.println(composedFunction.apply("abc")); //false
Functions - toStringFunction
The toStringFunction returns a Function<Object, String>. It just takes an input and returns the result of invoking the toString() on it. It will throw a NullPointerException when passed a null.
Function<Object, String> stringFunction = Functions.toStringFunction();
System.out.println(stringFunction.apply("a")); //a
This is not consistent with equals i.e., when we have two instances say a and b which are equal (i.e., a.equals(b) is true), then calling Function.apply with either of them should return the same result. But since this calls the toString() on the passed input, it violates this because two equal objects can have different toString representations.
System.out.println(stringFunction.apply(Set.of(1, 2))); //[1, 2]
System.out.println(stringFunction.apply(Set.of(2, 1))); //[2, 1]
Conclusion
In this post, we learnt about the Google Guava Functions utility class. Check out the other google-guava posts here as well.