Primitive Type Specializations of BiConsumer<T, U>
Table 13.6 shows the generic functional interfaces ObjIntConsumer<T>, ObjLongConsumer<T>, and ObjDoubleConsumer<T> that are specializations of the BiConsumer<T, U> interface. The functional method accept() of these primitive type specializations takes two arguments: One is an object of type T and the other is a primitive value. These functional interfaces are not subinterfaces of the BiConsumer<T, U> interface, and they do not provide default methods to chain consumers.
The code below shows a new version of resizing a StringBuilder written earlier, where the required length was hard-coded in the lambda expression definition and could not be changed. Using an ObjIntConsumer<StringBuilder>, the required length can be passed as a parameter in the definition of the lambda expression, as shown below:
ObjIntConsumer<StringBuilder> resizeSB2 = (sb, len) -> sb.setLength(len);
StringBuilder sb2 = new StringBuilder(“bananarama”);
resizeSB2.accept(sb2, 6); // The required length passed as a parameter.
System.out.println(“StringBuilder resized: ” + sb2);
// StringBuilder resized: banana
13.8 Functions
The Function<T, R> interface represents a function or an operation that transforms an argument object to a result object, where the object types need not be the same. From Table 13.7, we see that its functional method apply() has the type T -> R—that is, it takes an argument of type T and returns a result of type R.
Table 13.7 also shows specialized functions for primitive types, together with their functional methods. They do not define any default methods.
The BiFunction<T, U, R> interface and its primitive type versions are discussed in §13.9, p. 717. The specialized versions UnaryOperator<T> and BinaryOperator<T>, which provide functions where the arguments and the result are of the same type, are discussed in §13.10, p. 720, and §13.11, p. 721, respectively.
Table 13.7 Functions
Functional interface (T, U, and R are type parameters) | Functional method | Default methods unlesss otherwise |
Function<T, R> | apply: T -> R | compose(), andThen(), static identity() |
IntFunction<R> | apply: int -> R | – |
LongFunction<R> | apply: long -> R | – |
DoubleFunction<R> | apply: double -> R | – |
ToIntFunction<T> | applyAsInt: T -> int | – |
ToLongFunction<T> | applyAsLong: T -> long | – |
ToDoubleFunction<T> | applyAsDouble: T -> double | – |
IntToLongFunction | applyAsLong: int -> long | – |
IntToDoubleFunction | applyAsDouble: int -> double | – |
LongToIntFunction | applyAsInt: long -> int | – |
LongToDoubleFunction | applyAsDouble: long -> double | – |
DoubleToIntFunction | applyAsInt: double -> int | – |
DoubleToLongFunction | applyAsLong: double -> long | – |
Example 13.6 illustrates defining and using functions. The first lambda expression tests whether an integer is in a given range. It has the type Integer -> Boolean, compatible with the function type of the Function<Integer, Boolean> interface. Note that it returns a Boolean, as opposed to a lambda expression which implements a Predicate<T> that always returns a boolean value.
Function<Integer, Boolean> boolExpr = i -> 50 <= i && i < 100;
System.out.println(“Boolean expression is: ” + boolExpr.apply(99));
// Boolean expression is: true
Function<Integer, Double> milesToKms = miles -> 1.6 * miles;
System.out.printf(“%dmi = %.2fkm%n”, 24, milesToKms.apply(24));
// 24mi = 38.40km
The second lambda expression above converts miles to kilometers. It has the type Integer -> Double, compatible with the function type of the Function<Integer, Double> interface.
The method listBuilder() in Example 13.6 creates a list from an array by applying a Function<T, R> to each array element. The Function<T, R> is passed as an argument to the method.
String[] strArray = {“One”, “Two”, “Three”, “Four”};
List<StringBuilder> sbList = listBuilder(strArray, s -> new StringBuilder(s));
System.out.println(“Build StringBuilder list: ” + sbList);
// Build StringBuilder list: [One, Two, Three, Four]
The example above creates a list of StringBuilder from an array of String. The signature of the method call can be inferred to be the following:
listBuilder(String[], String -> StringBuilder)
with the type parameters T and R in the generic type Function<T, R> inferred as String and StringBuilder, respectively, resulting in the parameterized type Function<String, StringBuilder>.
The second example creates a list of Integers from an array of Strings, where the functional interface parameter in the method call is inferred to be Function<String, Integer>.
List<Integer> intList = listBuilder(strArray, s -> s.length());
System.out.println(“Build Integer list: ” + intList);
// Build Integer list: [3, 3, 5, 4]
Example 13.6 Implementing Functions
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
import java.util.function.IntFunction;
import java.util.function.IntToDoubleFunction;
import java.util.function.ToIntFunction;
public class FunctionClient {
public static void main(String[] args) {
// Examples of Function<T,R>:
Function<Integer, Boolean> boolExpr = i -> 50 <= i && i < 100;
System.out.println(“Boolean expression is: ” + boolExpr.apply(99));
// Boolean expression is: true
Function<Integer, Double> milesToKms = miles -> 1.6 * miles;
System.out.printf(“%dmi = %.2fkm%n”, 24, milesToKms.apply(24));
// 24mi = 38.40km
// Create a list of StringBuilders from an array of Strings.
String[] strArray = {“One”, “Two”, “Three”, “Four”};
List<StringBuilder> sbList = listBuilder(strArray, s -> new StringBuilder(s));
System.out.println(“Build StringBuilder list: ” + sbList);
// Build StringBuilder list: [One, Two, Three, Four]
// Create a list of Integers from an array of Strings.
List<Integer> intList = listBuilder(strArray, s -> s.length());
System.out.println(“Build Integer list: ” + intList);
// Build Integer list: [3, 3, 5, 4]
/* Composing unary functions. */
Function<String, String> f = s -> s + “-One”; // (1)
Function<String, String> g = s -> s + “-Two”; // (2)
// Using compose() and andThen() methods.
System.out.println(f.compose(g).apply(“Three”)); // (3) Three-Two-One
System.out.println(g.andThen(f).apply(“Three”)); // (4) Three-Two-One
System.out.println(f.apply(g.apply(“Three”))); // (5) Three-Two-One
System.out.println();
System.out.println(f.andThen(g).apply(“Three”)); // (6) Three-One-Two
System.out.println(g.compose(f).apply(“Three”)); // (7) Three-One-Two
System.out.println(g.apply(f.apply(“Three”))); // (8) Three-One-Two
System.out.println();
// Examples of primitive unary functions.
IntFunction<String> intToStr = i -> Integer.toString(i);
System.out.println(intToStr.apply(2021)); // 2021
ToIntFunction<String> strToInt = str -> Integer.parseInt(str);
System.out.println(strToInt.applyAsInt(“2021”)); // 2021
IntToDoubleFunction celsiusToFahrenheit = celsius -> 1.8 * celsius + 32.0;
System.out.printf(“%d Celsius = %.1f Fahrenheit%n”,
37, celsiusToFahrenheit.applyAsDouble(37));
// 37 Celsius = 98.6 Fahrenheit
}
/**
* Create a list from an array by applying a Function to each array element.
* @param arrayT Array to use for elements
* @param func Function to apply to each array element
* @return List that is created
*/
public static <T, R> List<R> listBuilder(T[] arrayT, Function<T, R> func) {
List<R> listR = new ArrayList<>();
for (T t : arrayT) {
listR.add(func.apply(t));
}
return listR;
}
}
Output from the program:
Boolean expression is: true
24mi = 38.40km
Build StringBuilder list: [One, Two, Three, Four]
Build Integer list: [3, 3, 5, 4]
Three-Two-One
Three-Two-One
Three-Two-One
Three-One-Two
Three-One-Two
Three-One-Two
2021
2021
37 Celsius = 98.6 Fahrenheit