Primitive Type Specializations of BiConsumer – Functional-Style Programming

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:

Click here to view code image

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 methodDefault methods unlesss otherwise
Function<T, R>apply: T -> Rcompose(), 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
IntToLongFunctionapplyAsLong: int -> long
IntToDoubleFunctionapplyAsDouble: int -> double
LongToIntFunctionapplyAsInt: long -> int
LongToDoubleFunctionapplyAsDouble: long -> double
DoubleToIntFunctionapplyAsInt: double -> int
DoubleToLongFunctionapplyAsLong: 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.

Click here to view code image

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.

Click here to view code image

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:

Click here to view code image

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>.

Click here to view code image

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

Click here to view code image

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:

Click here to view code image

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

Leave a Comment