Showing posts with label Java 8. Show all posts
Showing posts with label Java 8. Show all posts

Thursday, January 31, 2019

JDK 9/JEP 280: String Concatenations Will Never Be the Same

JEP 280 ("Indify String Concatenation") was implemented in conjunction with JDK 9 and, according to its "Summary" section, "Change[s] the static String-concatenation bytecode sequence generated by javac to use invokedynamic calls to JDK library functions." The impact this has on string concatenation in Java is most easily seen by looking at the javap output of classes using string concatenation that are compiled in pre-JDK 9 and post-JDK 9 JDKs.

The following simple Java class named "HelloWorldStringConcat" will be used for the first demonstration.

Contrasting of the differences in -verbose output from javap for the HelloWorldStringConcat class's main(String) method when compiled with JDK 8 (AdoptOpenJDK) and JDK 11 (Oracle OpenJDK) is shown next. I have highlighted some key differences.

JDK 8 javap Output

Classfile /C:/java/examples/helloWorld/classes/dustin/examples/HelloWorldStringConcat.class
  Last modified Jan 28, 2019; size 625 bytes
  MD5 checksum 3e270bafc795b47dbc2d42a41c8956af
  Compiled from "HelloWorldStringConcat.java"
public class dustin.examples.HelloWorldStringConcat
  minor version: 0
  major version: 52
     . . .
  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=4, locals=1, args_size=1
         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: new           #3                  // class java/lang/StringBuilder
         6: dup
         7: invokespecial #4                  // Method java/lang/StringBuilder."":()V
        10: ldc           #5                  // String Hello,
        12: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        15: aload_0
        16: iconst_0
        17: aaload
        18: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        21: invokevirtual #7                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        24: invokevirtual #8                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        27: return

JDK 11 javap Output

Classfile /C:/java/examples/helloWorld/classes/dustin/examples/HelloWorldStringConcat.class
  Last modified Jan 28, 2019; size 908 bytes
  MD5 checksum 0e20fe09f6967ba96124abca10d3e36d
  Compiled from "HelloWorldStringConcat.java"
public class dustin.examples.HelloWorldStringConcat
  minor version: 0
  major version: 55
     . . .
  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
    Code:
      stack=3, locals=1, args_size=1
         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: aload_0
         4: iconst_0
         5: aaload
         6: invokedynamic #3,  0              // InvokeDynamic #0:makeConcatWithConstants:(Ljava/lang/String;)Ljava/lang/String;
        11: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        14: return

The "Description" section of JEP 280 describes this difference: "The idea is to replace the entire StringBuilder append dance with a simple invokedynamic call to java.lang.invoke.StringConcatFactory, that will accept the values in the need of concatenation." This same section shows a similar comparison of compiled output for a similar string concatenation example.

The compiled output from JDK 11 for the simple string concatenation is not just fewer lines than its JDK 8 counterpart; it also has fewer "expensive" operations. Potential performance improvement can be gained from not needing to wrap primitive types, and not needing to instantiate a bunch of extra objects. One of the primary motivations for this change was to "lay the groundwork for building optimized String concatenation handlers, implementable without the need to change the Java-to-bytecode compiler" and to "enable future optimizations of String concatenation without requiring further changes to the bytecode emitted by javac."

JEP 280 Does Not Affect StringBuilder or StringBuffer

There's an interesting implication of this in terms of using StringBuffer (which I have a difficult time finding a good use for anyway) and StringBuilder. It was a stated "Non-Goal" of JEP 280 to not "introduce any new String and/or StringBuilder APIs that might help to build better translation strategies." Related to this, for simple string concatenations like that shown in the code example at the beginning of this post, explicit use of StringBuilder and StringBuffer will actually preclude the ability for the compiler to make use of the JEP 280-introduced feature discussed in this post.

The next two code listings show similar implementations to the simple application shown above, but these use StringBuilder and StringBuffer respectively instead of string concatenation. When javap -verbose is executed against these classes after they are compiled with JDK 8 and with JDK 11, there are no significant differences in the main(String[]) methods.

Explicit StringBuilder Use in JDK 8 and JDK 11 Are The Same

JDK 8 javap Output for HelloWorldStringBuilder.main(String[])

Classfile /C:/java/examples/helloWorld/classes/dustin/examples/HelloWorldStringBuilder.class
  Last modified Jan 28, 2019; size 627 bytes
  MD5 checksum e7acc3bf0ff5220ba5142aed7a34070f
  Compiled from "HelloWorldStringBuilder.java"
public class dustin.examples.HelloWorldStringBuilder
  minor version: 0
  major version: 52
     . . .
  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=4, locals=1, args_size=1
         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: new           #3                  // class java/lang/StringBuilder
         6: dup
         7: invokespecial #4                  // Method java/lang/StringBuilder."":()V
        10: ldc           #5                  // String Hello,
        12: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        15: aload_0
        16: iconst_0
        17: aaload
        18: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        21: invokevirtual #7                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        24: invokevirtual #8                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        27: return

JDK 11 javap Output for HelloWorldStringBuilder.main(String[])

Classfile /C:/java/examples/helloWorld/classes/dustin/examples/HelloWorldStringBuilder.class
  Last modified Jan 28, 2019; size 627 bytes
  MD5 checksum d04ee3735ce98eb6237885fac86620b4
  Compiled from "HelloWorldStringBuilder.java"
public class dustin.examples.HelloWorldStringBuilder
  minor version: 0
  major version: 55
     . . .
  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
    Code:
      stack=4, locals=1, args_size=1
         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: new           #3                  // class java/lang/StringBuilder
         6: dup
         7: invokespecial #4                  // Method java/lang/StringBuilder."":()V
        10: ldc           #5                  // String Hello,
        12: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        15: aload_0
        16: iconst_0
        17: aaload
        18: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        21: invokevirtual #7                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        24: invokevirtual #8                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        27: return

Explicit StringBuffer Use in JDK 8 and JDK 11 Are The Same

JDK 8 javap Output for HelloWorldStringBuffer.main(String[])

Classfile /C:/java/examples/helloWorld/classes/dustin/examples/HelloWorldStringBuffer.class
  Last modified Jan 28, 2019; size 623 bytes
  MD5 checksum fdfb90497db6a3494289f2866b9a3a8b
  Compiled from "HelloWorldStringBuffer.java"
public class dustin.examples.HelloWorldStringBuffer
  minor version: 0
  major version: 52
     . . .
  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=4, locals=1, args_size=1
         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: new           #3                  // class java/lang/StringBuffer
         6: dup
         7: invokespecial #4                  // Method java/lang/StringBuffer."":()V
        10: ldc           #5                  // String Hello,
        12: invokevirtual #6                  // Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
        15: aload_0
        16: iconst_0
        17: aaload
        18: invokevirtual #6                  // Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
        21: invokevirtual #7                  // Method java/lang/StringBuffer.toString:()Ljava/lang/String;
        24: invokevirtual #8                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        27: return

JDK 11 javap Output for HelloWorldStringBuffer.main(String[])

Classfile /C:/java/examples/helloWorld/classes/dustin/examples/HelloWorldStringBuffer.class
  Last modified Jan 28, 2019; size 623 bytes
  MD5 checksum e4a83b6bb799fd5478a65bc43e9af437
  Compiled from "HelloWorldStringBuffer.java"
public class dustin.examples.HelloWorldStringBuffer
  minor version: 0
  major version: 55
     . . .
  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
    Code:
      stack=4, locals=1, args_size=1
         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: new           #3                  // class java/lang/StringBuffer
         6: dup
         7: invokespecial #4                  // Method java/lang/StringBuffer."":()V
        10: ldc           #5                  // String Hello,
        12: invokevirtual #6                  // Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
        15: aload_0
        16: iconst_0
        17: aaload
        18: invokevirtual #6                  // Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
        21: invokevirtual #7                  // Method java/lang/StringBuffer.toString:()Ljava/lang/String;
        24: invokevirtual #8                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        27: return

JDK 8 and JDK 11 Handling of Looped String Concatenation

For my last example of JEP 280 changes in action, I use a code sample that may offend some Java developers' sensibilities and perform a string concatenation within a loop. Keep in mind this is just an illustrative example and all will be okay, but don't try this at home.

JDK 8 javap Output for HelloWorldStringConcatComplex.main(String[])

Classfile /C:/java/examples/helloWorld/classes/dustin/examples/HelloWorldStringConcatComplex.class
  Last modified Jan 30, 2019; size 766 bytes
  MD5 checksum 772c4a283c812d49451b5b756aef55f1
  Compiled from "HelloWorldStringConcatComplex.java"
public class dustin.examples.HelloWorldStringConcatComplex
  minor version: 0
  major version: 52
     . . .
  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=3, args_size=1
         0: ldc           #2                  // String Hello
         2: astore_1
         3: iconst_0
         4: istore_2
         5: iload_2
         6: bipush        25
         8: if_icmpge     36
        11: new           #3                  // class java/lang/StringBuilder
        14: dup
        15: invokespecial #4                  // Method java/lang/StringBuilder."":()V
        18: aload_1
        19: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        22: iload_2
        23: invokevirtual #6                  // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
        26: invokevirtual #7                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        29: astore_1
        30: iinc          2, 1
        33: goto          5
        36: getstatic     #8                  // Field java/lang/System.out:Ljava/io/PrintStream;
        39: aload_1
        40: invokevirtual #9                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        43: return

JDK 11 javap Output for HelloWorldStringConcatComplex.main(String[])

Classfile /C:/java/examples/helloWorld/classes/dustin/examples/HelloWorldStringConcatComplex.class
  Last modified Jan 30, 2019; size 1018 bytes
  MD5 checksum 967fef3e7625965ef060a831edb2a874
  Compiled from "HelloWorldStringConcatComplex.java"
public class dustin.examples.HelloWorldStringConcatComplex
  minor version: 0
  major version: 55
     . . .
  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=3, args_size=1
         0: ldc           #2                  // String Hello
         2: astore_1
         3: iconst_0
         4: istore_2
         5: iload_2
         6: bipush        25
         8: if_icmpge     25
        11: aload_1
        12: iload_2
        13: invokedynamic #3,  0              // InvokeDynamic #0:makeConcatWithConstants:(Ljava/lang/String;I)Ljava/lang/String;
        18: astore_1
        19: iinc          2, 1
        22: goto          5
        25: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
        28: aload_1
        29: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        32: return

In the presentation "Enough java.lang.String to Hang Ourselves ...," Dr. Heinz M. Kabutz and Dmitry Vyazelenko discuss the JEP 280-introduced changes to Java string concatenation and summarize it succinctly, "+ is no longer compiled to StringBuilder." In their "Lessons from Today" slide, they state, "Use + instead of StringBuilder where possible" and "recompile classes for Java 9+."

The changes implemented in JDK 9 for JEP 280 "will enable future optimizations of String concatenation without requiring further changes to the bytecode emitted by javac." Interestingly, it was recently announced that JEP 348 ("Java Compiler Intrinsics for JDK APIs") is now a Candidate JEP and it aims to use a similar approach for compiling methods String::format and Objects::hash.

Monday, July 9, 2018

Deferred Execution with Java's Predicate

In the previous posts "Deferred Execution with Java's Supplier" and "Deferred Execution with Java's Consumer", I looked at easily deferring execution in Java via standard Java APIs that accept, respectively, Suppliers and Consumers. In this post, I take a similar look at how standard JDK-provided APIs allow for deferred execution via the standard functional interface Predicate. The Predicate is described in its Javadoc, "Represents a predicate (boolean-valued function) of one argument." In other words, a Predicate is like a JDK-supplied Function, but with its return value limited to either true or false.

Perhaps the most common application of Predicate in the standard Java APIs is in the context of filters. Several of the examples in this post will demonstrate use of Predicate in conjunction with filtering methods on instances of Optional and on instances of Stream.

Optional.filter(Predicate)

The behavior of the Optional class's filter(Predicate) method is described this way by its Javadoc documentation, "If a value is present, and the value matches the given predicate, returns an Optional describing the value, otherwise returns an empty Optional." In other words, Optional.filter(Predicate) returns an Optional that will be empty if either the original Optional was empty or if the Predicate applied to the original and present Optional resolves to false. Otherwise, if the original Optional does have a "present" value and the Predicate applied to that value returns true, the returned Optional will also have the same "present" value. This is illustrated in the next code listing (full source is available on GitHub).

Optional.filter(Predicate) Demonstrated

/**
 * Demonstrate use of {@code Optional.filter(Predicate)} on an
 * {@code Optional<Boolean>}.
 */
public static void demonstrateOptionalFilterOnBoolean()
{
   out.print("\nfalse: ");
   getOptionalBoolean(false).filter(b -> b).ifPresent(out::print);
   out.print("\ntrue:  ");
   getOptionalBoolean(true).filter(b -> b).ifPresent(out::print);
   out.print("\nnull:  ");
   getOptionalBoolean(null).filter(b -> b).ifPresent(out::print);
}

/**
 * Demonstrate use of {@code Optional.filter(Predicate)} on an
 * {@code Optional<Float>}.
 */
public static void demonstrateOptionalFilterOnFloat()
{
   out.print("\n3.14: ");
   getOptionalFloat(3.14f).filter(f -> f > 0.0).ifPresent(out::print);
   out.print("\n-2.5: ");
   getOptionalFloat(-2.5f).filter(f -> f > 0.0).ifPresent(out::print);
   out.print("\nnull: ");
   getOptionalFloat(null).filter(f -> f > 0.0).ifPresent(out::print);
}

The two methods in the above code listing demonstrate use of Optional.filter(Predicate) on a lambda expression that results in a direct boolean result and on a lambda expression that results in a boolean result based on numerical comparison. In one case, the Predicate is the boolean and in the other case the Predicate is the numeric comparison.

Stream.filter(Predicate)

The Stream interface's method filter(Predicate) works similarly to the Optional class's method of the same name. The next code listing demonstrates application of Stream.filter(Predicate).

Stream.filter(Predicate) Demonstrated

/**
 * Demonstrates use of {@code Stream.filter(Predicate}}.
 */
public static void demonstrateStreamFilter()
{
   final int maximum = 100;
   out.println("\nThe probable prime numbers between 1 and " + maximum + " are:");
   final Stream<BigInteger> bigIntegers = getConsecutiveBigIntegers(maximum);
   bigIntegers.filter(bi -> bi.isProbablePrime(100)).forEach(pp -> out.println(" " + pp));
}

The above code listing is not intended to demonstrate the best approach to identifying prime numbers in Java. Instead, it's intended to demonstrate how filter(Predicate) can be invoked on a Stream to narrow down elements of that Stream to only those matching the Predicate.

For my next illustration of Stream.filter(Predicate), I use the Pattern class's convenient method asPredicate() to supply the instance of the Predicate to be supplied to both examples using Stream.filter(Predicate).

Stream.filter(Predicate) with Pattern.asPredicate() Demonstrated

/**
 * Demonstrates use of {@code Pattern.asPredicate()} to provide
 * a {@code Predicate} that can be used with {@code Stream.filter()}.
 */
public static void demonstratePatternAsPredicateInFilter()
{
   final long count
      = getPotentialTelephoneNumbers().stream()
         .filter(PATTERN.asPredicate())
         .peek(out::println)
         .count();
   out.println(count + " valid telephone numbers.");
}

Collection.removeIf(Predicate)

The Collection interface specifies (and implements as a default method) the useful method removeIf(Predicate). There are also multiple implementations of Collection that implement their own overridden versions of removeIf(Predicate) that include ArrayDeque.removeIf(Predicate), ArrayList.removeIf(Predicate), and Vector.removeIf(Predicate).

The next code listing demonstrates two examples of Collection.removeIf(Predicate) in action. The first example uses the method Predicate.negate() to negate the expected regular expression pattern so that the elements removed from the collection are those that do NOT match the regular expression. The second example performs similar functionality, but takes advantage of the JDK 11-introduced 'not' method to perform this negation.

Collection.removeIf(Predicate) with Negated Pattern.asPredicate() Demonstrated

/**
 * Demonstrates use of {@code Collection.removeIf(Predicate)}
 * in conjunction with {@code Predicate.negate()}.
 */
public static void demonstrateCollectionRemoveIf()
{
   final Set<String> telephoneNumbers = new HashSet<>(getPotentialTelephoneNumbers());
   telephoneNumbers.removeIf(PATTERN.asPredicate().negate());
   out.println(telephoneNumbers);
}

/**
 * Demonstrates use of {@code Collection.removeIf(Predicate)}
 * in conjunction with JDK 11-introduced {@code Predicate.not()}.
 */
public static void demonstrateCollectionRemoveIfWithJdk11Not()
{
   final Set<String> telephoneNumbers = new HashSet<>(getPotentialTelephoneNumbers());
   telephoneNumbers.removeIf(not(PATTERN.asPredicate()));
   out.println(telephoneNumbers);
}

Stream.allMatch(Predicate)

The Stream interface's method allMatch(Predicate) returns true if every single element in the stream matches the provided Predicate. If even a single element does not match the Predicate, the method returns false.

Stream.allMatch(Predicate) Demonstrated

/**
 * Demonstrate use of {@code Stream.allMatch(Predicate)}.
 */
public static void demonstrateStreamAllMatch()
{
   final Set<String> names = getNames();
   final boolean allNamesSixDigits = names.stream()
      .allMatch(name -> name.length() == 6);
   out.println("Are all names " + names + " six digits? " + allNamesSixDigits);
}

Stream.anyMatch(Predicate)

The Stream.anyMatch(Predicate) method returns true if at least one of its elements matches the Predicate and returns false if none of its elements match the Predicate.

Stream.anyMatch(Predicate) Demonstrated

/**
 * Demonstrate use of {@code Stream.anyMatch(Predicate)}.
 */
public static void demonstrateStreamAnyMatch()
{
   final Set<String> names = getNames();
   final boolean anyNamesSixDigits = names.stream()
      .anyMatch(name -> name.length() == 6);
   out.println("Are any names " + names + " six digits? " + anyNamesSixDigits);
}

Stream.noneMatch(Predicate)

The Stream.noneMatch(Predicate) method returns true when no elements in stream match the Predicate and returns false if at least one element in the stream DOES match the Predicate.

Stream.noneMatch(Predicate) Demonstrated

/**
 * Demonstrate use of {@code Stream.noneMatch(Predicate)}.
 */
public static void demonstrateStreamNoneMatch()
{
   final Set<String> names = getNames();
   final boolean noNamesSixDigits = names.stream()
      .noneMatch(name -> name.length() == 6);
   out.println("Are no names " + names + " six digits? " + noNamesSixDigits);
   final boolean noNamesFourDigits = names.stream()
      .noneMatch(name -> name.length() == 4);
   out.println("Are no names " + names + " four digits? " + noNamesFourDigits);
}

Collectors.partitioningBy(Predicate)

Although there are several more JDK APIs that use Predicate, I wrap this post up with discussion of and an example of using Collectors.partitioningBy(Predicate). This interesting method divides up all elements in the stream it is invoked upon into two groups with one group associated with key Boolean.TRUE (elements that match the Predicate) and with one group associated with the key Boolean.FALSE (those elements that did not match the Predicate). The next code listing takes advantage of this to divide integers up into even numbers and odd numbers.

Collectors.partitioningBy(Predicate) Demonstrated

/**
 * Demonstrate use of {@code Collectors.partitioningBy(Predicate)}.
 */
public static void demonstrateCollectorsPartitioningBy()
{
   final Map<Boolean, List<Integer>> evensAndOdds
      = getConsecutiveIntegers(100)
         .collect(Collectors.partitioningBy(integer -> integer % 2 == 0));
   out.println("Evens: " + evensAndOdds.get(Boolean.TRUE));
   out.println("Odds:  " + evensAndOdds.get(Boolean.FALSE));
}

I used several "helper" methods in the above code examples that are not shown in this post. These "helper" methods and all of the examples shown in this post are available on GitHub.

Java's standard functional interface Predicate is a specialized version of fellow built-in Java functional interface Function that arguably deserves its own specialization because the true/false return status is so commonly useful for representing the conditions in which certain functionality applies or does not apply. This post has demonstrated several instances in the JDK where Predicate is used to determine which stream elements apply, whether or not an Optional applies, and to divide up stream elements into those that satisfy the predicate and those that do not. Along the way, convenience methods such as Pattern.asPredicate() and Predicate.not() were also demonstrated.

Tuesday, June 26, 2018

Deferred Execution with Java's Consumer

In an earlier blog post ("Deferred Execution with Java's Supplier") I referenced Cay Horstmann's statement in the book "Java SE8 for the Really Impatient" regarding lambda expressions, "The point of all lambdas is deferred execution." Horstmann wrote an article called "Lambda Expressions in Java 8" for Dr. Dobb's magazine in its final year in which he wrote a similar statement using different terminology, "A lambda expression is a block of code that you can pass around so it can be executed later, just once or multiple times."

In that earlier post, I looked at how the standard functional interface Supplier is used with lambda expressions in the JDK to support deferred execution for cases where a single value is "supplied only when necessary" and without any argument passed to it. In this post, I focus on JDK-provided examples of using the Consumer standard functional interface to "consume" or "process" a particular code block "only when necessary." Whereas the Supplier accepts no arguments and returns exactly one response, the Consumer accepts one or more arguments and returns no response. The method invoked on a Supplier is the get() method and it is the accept(T) method for a Consumer. By definition, the Consumer is expected to have "side effects" as it "consumes" the provided code block.

There are numerous Consumer-style standard functional interfaces supplied in the java.util.function package. None of these returns a result (that's why they're consumers!), but they differ in the number and types of arguments they accept (but they all accept at least one argument). These are listed here:

  • Consumer - General Consumer that accepts a single argument and will be the center of attention for most of this post's examples.
  • BiConsumer - Accepts two arguments instead of one ("two-arity specialization of Consumer")
  • DoubleConsumer - Specialized Consumer intended for primitive doubles
  • IntConsumer - Specialized consumer for primitive ints
  • LongConsumer - Specialized Consumer intended for primitive longs
  • ObjDoubleConsumer - Specialized Consumer that accepts two arguments, with the first of type Object and the second of type double
  • ObjIntConsumer - Specialized Consumer that accepts two arguments, with the first of type Object and the second of type int
  • ObjLongConsumer - Specialized Consumer that accepts two arguments, with the first of type Object and the second of type long

The remainder of this post will look at a subset of the JDK uses of Consumer and related classes to help demonstrate how and when they are useful.

Peeking at Flow of Stream Elements

In the blog post "Peeking Inside Java Streams with Stream.peek," I discussed the intermediate operation Stream.peek(Consumer) that can be used to view the flowing elements of a stream. This can be very useful for understanding what the various stream operations are doing to their respective stream elements. A common way to do this is to have the Consumer provided to the peek method be a call to System.out.println that prints the currently processed stream element to standard output (or log the element or print it to standard error). An example of this is provided in the Javadoc documentation for the Stream.peek(Consumer) method:

Stream.of("one", "two", "three", "four")
   .filter(e -> e.length() > 3)
   .peek(e -> System.out.println("Filtered value: " + e))
   .map(String::toUpperCase)
   .peek(e -> System.out.println("Mapped value: " + e))
   .collect(Collectors.toList());

Because the various overloaded versions of the println(-) method accept a parameter but do not return anything, they fit perfectly with the "Consumer" concept.

Specifying Action Upon Iterated Stream Elements

While Stream.peek(Consumer) is an intermediate operation, Stream provides two other methods that accept a Consumer that are both terminal operations and are both "for each" methods. The method Stream.forEach​(Consumer) is a method that performs the action specified by the provided Consumer in an "explicitly nondeterministic" manner on the stream's elements. The method Stream.forEachOrdered(Consumer) performs the action specified by the provided Consumer in "the encounter order" of the stream if that stream has an encounter order. In both methods' cases, the Consumer-based "action" should be "non-interfering." Both methods are demonstrated below.

Set.of("one", "two", "three", "four")
   .stream()
   .forEach(i -> out.println(i.toUpperCase()));

Stream.of("one", "two", "three", "four")
   .forEach(i -> out.println(i.toUpperCase()));

List.of("one", "two", "three", "four")
   .stream()
   .forEachOrdered(i -> out.println(i.toUpperCase()));

Stream.of("one", "two", "three", "four")
   .forEachOrdered(i -> out.println(i.toUpperCase()));

The above examples look and very similar. The most obvious situation in which forEach could lead to dramatically different results than forEachOrdered is when parallel stream processing is employed. In that case, it makes most sent to use forEach instead of forEachOrdered.

Specifying Action Upon Iterable Elements

The previous code examples showed using Stream.forEach(Consumer) methods to iterate a stream. The examples also demonstrated doing this against a Set and List by first calling stream() on these collections. There are convenience methods, however, that are defined by Iterable and implemented by these collection implementations which accept a Consumer and allow for iteration of that collection using the forEach method. Examples of this are shown in the next code listing.

Set.of("one", "two", "three", "four")
   .forEach(i -> out.println(i.toUpperCase()));
List.of("one", "two", "three", "four")
   .forEach(i -> out.println(i.toUpperCase()));

Although I used collections in my example above, anything that implements Iterable will generally support the forEach method (or be in violation of the interface's advertised contract).

Specifying Action Upon Iteration of Map Entries

Although Java's Map interface does not extend the Iterable interface like Set and List do, the Java Map was still provided with a similar capability to specify a consumer to "consume" each entry in the Map. Because a Map has two input arguments (key and value), its forEach method accepts a BiConsumer instead of the Consumer discussed so far in this post. A simple example is shown next.

Map.of("Denver", "Colorado",
       "Cheyenne", "Wyoming",
       "Salt Lake City", "Utah",
       "Boise", "Idaho")
   .forEach((c, s) -> out.println(c + " is the capital of " + s));

Walking the Stack

The StackWalker is a welcome addition to JDK 9 that provides a thread-safe approach to perusing a stack trace and is a significant improvement over the StackTraceElement approach. It's arguably more common for developers to use StackWalker.walk(Function), but this post is about Consumer and so the focus is on StackWalker.forEach(Consumer). This method is similar to the previously discussed Stream.forEach and Iterable.forEach methods and is demonstrated in the next code listing.

StackWalker.getInstance().forEach(out::println);

Although there are many more JDK uses of Consumer, of BiConsumer, and of the other types of standard Consumer-style functional interfaces, the last examples I'll cover in this post come from the Optional class.

Applying Only When Present

The methods Optional.ifPresent(Consumer) and Optional.ifPresentOrElse(Consumer) defer the execution of the provided Consumers such that the provided Consumer will only be invoked if the Optional is not "empty" (contains a non-null value). This is a simple but powerful concept and the simplistic and contrived examples show how they work.

public void demonstrateOptionalIfPresent()
{
   getMiddleName(true).ifPresent(n -> out.println("Middle Name: " + n));
}

public void demonstrateOptionalIfPresentOrElse()
{
   getMiddleName(false).ifPresentOrElse(
      n -> out.println("Middle Name: " + n),
      () -> displayMissingMiddleName());
}

private Optional<String> getMiddleName(final boolean present)
{
   return present ? Optional.of("Wayne") : Optional.empty();
}

private void displayMissingMiddleName()
{
   out.println("No middle name provided!");
}

As the above code listing demonstrates, both Optional.ifPresent and JDK 9-introduced Optional.ifPresentOrElse() only invoke the provided Consumer if the Optional is not empty. If the Optional is empty, the ifPresent method does nothing and the ifPresentOrElse invokes the second argument (a Runnable).

The standard Java functional interfaces that accept one or more arguments and return no result include the general Consumer as well as some specialized consumers. These are useful for deferring execution until a given condition occurs (such as being iterated upon or being determined to be present) and the behavior to apply when that condition occurs involves one or more input arguments and no need to provide a response. The source code examples shown in this post are available on GitHub.

Thursday, June 14, 2018

JDK 8 BigInteger Exact Narrowing Conversion Methods

In the blog post "Exact Conversion of Long to Int in Java," I discussed using Math.toIntExact(Long) to exactly convert a Long to an int or else throw an ArithmeticException if this narrowing conversion is not possible. That method was introduced with JDK 8, which also introduced similar narrowing conversion methods to the BigInteger class. Those BigInteger methods are the topic of this post.

BigInteger had four new "exact" methods added to it in JDK 8:

As described above, each of these four "exact" methods added to BigInteger with JDK 8 allow for the BigInteger's value to be narrowed to the data type in the method name, if that is possible. Because all of these types (byte, short, int, and long) have smaller ranges than BigInteger, it's possible in any of these cases to have a value in BigDecimal with a magnitude larger than that which can be represented by any of these four types. In such a case, all four of these "Exact" methods throw an ArithmeticException rather than quietly "forcing" the bigger value into the smaller representation (which is typically a nonsensical number for most contexts).

Examples of using these methods can be found on GitHub. When those examples are executed, the output looks like this:

===== Byte =====
125 => 125
126 => 126
127 => 127
128 => java.lang.ArithmeticException: BigInteger out of byte range
129 => java.lang.ArithmeticException: BigInteger out of byte range
===== Short =====
32765 => 32765
32766 => 32766
32767 => 32767
32768 => java.lang.ArithmeticException: BigInteger out of short range
32769 => java.lang.ArithmeticException: BigInteger out of short range
===== Int =====
2147483645 => 2147483645
2147483646 => 2147483646
2147483647 => 2147483647
2147483648 => java.lang.ArithmeticException: BigInteger out of int range
2147483649 => java.lang.ArithmeticException: BigInteger out of int range
===== Long =====
9223372036854775805 => 9223372036854775805
9223372036854775806 => 9223372036854775806
9223372036854775807 => 9223372036854775807
9223372036854775808 => java.lang.ArithmeticException: BigInteger out of long range
9223372036854775809 => java.lang.ArithmeticException: BigInteger out of long range

The addition of these "exact" methods to BigInteger with JDK 8 is a welcome one because errors associated with numeric narrowing and overflow can be subtle. It's nice to have an easy way to get an "exact" narrowing or else have the inability to do that narrowing exactly made obvious via an exception.

Tuesday, June 12, 2018

JDK 8 Versus JDK 10: Ternary/Unboxing Difference

A recent Nicolai Parlog (@nipafx) tweet caught my attention because it referenced an interesting StackOverflow discussion on a changed behavior between JDK 8 and JDK 10 and asked "Why?" The issue cited on the StackOverflow thread by SerCe ultimately came down to the implementation being changed between JDK 8 and JDK 10 to correctly implement the Java Language Specification.

The following code listing is (very slightly) adapted from the original example provided by SerCe on the StackOverflow thread.

Adapted Example That Behaves Differently in JDK 10 Versus JDK 8

public static void demoSerCeExample()
{
   try
   {
      final Double doubleValue = false ? 1.0 : new HashMap<String, Double>().get("1");
      out.println("Double Value: " + doubleValue);
   }
   catch (Exception exception)
   {
      out.println("ERROR in 'demoSerCeExample': " + exception);
   }
}

When the above code is compiled and executed with JDK 8, it generates output like this: Double Value: null

When the above code is compiled and executed with JDK 10, it generates output like this: ERROR in 'demoSerCeExample': java.lang.NullPointerException

In JDK 8, the ternary operator returned null for assigning to the local variable doubleValue, but in JDK 10 a NullPointerException is instead thrown for the same ternary statement.

Two tweaks to this example lead to some interesting observations. First, if the literal constant 1.0 expressed in the ternary operator is specified instead as Double.valueOf(1.0), both JDK 8 and JDK 10 set the local variable to null rather than throwing a NullPointerException. Second, if the local variable is declared with primitive type double instead of reference type Double, the NullPointerException is always thrown regardless of Java version and regardless of whether Double.valueOf(double) is used. This second observation makes sense, of course, because no matter how the object or reference is handled by the ternary operator, it must be dereferenced at some point to be assigned to the primitive double type and that will always result in a NullPointerException in the example.

The following table summarizes these observations:

Complete Ternary Statement Setting of Local Variable doubleValue
JDK 8 JDK 10
Double doubleValue
   =  false
    ? 1.0
    : new HashMap<String, Double>().get("1");
  
null NullPointerException
double doubleValue
   =  false
    ? 1.0
    : new HashMap<String, Double>().get("1");
  
NullPointerException NullPointerException
Double doubleValue
   =  false
    ? Double.valueOf(1.0)
    : new HashMap<String, Double>().get("1");
  
null null
double doubleValue
   =  false
    ? Double.valueOf(1.0)
    : new HashMap<String, Double>().get("1");
  
NullPointerException NullPointerException

The only approach that avoids NullPointerException in both versions of Java for this general ternary example is the version that declares the local variable as a reference type Double (no unboxing is forced) and uses Double.valueOf(double) so that reference Double is used throughout the ternary rather than primitive double. If the primitive double is implied by specifying only 1.0, then the Double returned by the Java Map is implicitly unboxed (dereferenced) in JDK 10 and that leads to the exception. According to Brian Goetz, JDK 10 brings the implementation back into compliance with the specification.

Exact Conversion of Long to Int in Java

With all the shiny things (lambda expressions, streams, Optional, the new Date/Time API, etc.) to distract my attention that came with JDK 8, I did not pay much attention to the addition of the method Math.toIntExact(). However, this small addition can be pretty useful in its own right.

The Javadoc documentation for Math.toIntExact​(long) states, "Returns the value of the long argument; throwing an exception if the value overflows an int." This is particularly useful in situations where one is given or already has a Long and needs to call an API that expects an int. It's best, of course, if the APIs could be changed to use the same datatype, but sometimes this is out of one's control. When one needs to force a Long into an int there is potential for integer overflow because the numeric value of the Long may have a greater magnitude than the int can accurately represent.

If one is told that a given Long will never be larger than what an int can hold, the static method Math.toIntExact(Long) is particularly useful because it will throw an unchecked ArithmeticException if that "exceptional" situation arises, making it obvious that the "exceptional" situation occurred.

When Long.intValue() is used to get an integer from a Long, no exception is thrown if integer overflow occurs. Instead, an integer is provided, but this value will rarely be useful due to the integer overflow. In almost every conceivable case, it's better to encounter a runtime exception that alerts one to the integer overflow than to have the software continue using the overflow number incorrectly.

As a first step in illustrating the differences between Long.intValue() and Math.toIntExact(Long), the following code generates a range of Long values from 5 less than Integer.MAX_VALUE to 5 more than Integer.MAX_VALUE.

Generating Range of Longs that Includes Integer.MAX_VALUE

/**
 * Generate {@code Long}s from range of integers that start
 * before {@code Integer.MAX_VALUE} and end after that
 * maximum integer value.
 *
 * @return {@code Long}s generated over range includes
 *    {@code Integer.MAX_VALUE}.
 */
public static List<Long> generateLongInts()
{
   final Long maximumIntegerAsLong = Long.valueOf(Integer.MAX_VALUE);
   final Long startingLong = maximumIntegerAsLong - 5;
   final Long endingLong = maximumIntegerAsLong + 5;
   return LongStream.range(startingLong, endingLong).boxed().collect(Collectors.toList());
}

The next code listing shows two methods that demonstrate the two previously mentioned approaches for getting an int from a Long.

Using Long.intValue() and Math.toIntExact(Long)

/**
 * Provides the {@code int} representation of the provided
 * {@code Long} based on an invocation of the provided
 * {@code Long} object's {@code intValue()} method.
 *
 * @param longRepresentation {@code Long} for which {@code int}
 *    value extracted with {@code intValue()} will be returned.
 * @return {@code int} value corresponding to the provided
 *    {@code Long} as provided by invoking the method
 *    {@code intValue()} on that provided {@code Long}.
 * @throws NullPointerException Thrown if the provided long
 *    representation is {@code null}.
 */
public static void writeLongIntValue(final Long longRepresentation)
{
   out.print(longRepresentation + " =>       Long.intValue() = ");
   try
   {
      out.println(longRepresentation.intValue());
   }
   catch (Exception exception)
   {
      out.println("ERROR - " + exception);
   }
}

/**
 * Provides the {@code int} representation of the provided
 * {@code Long} based on an invocation of {@code Math.toIntExact(Long)}
 * on the provided {@code Long}.
 *
 * @param longRepresentation {@code Long} for which {@code int}
 *    value extracted with {@code Math.toIntExact(Long)} will be
 *    returned.
 * @return {@code int} value corresponding to the provided
 *    {@code Long} as provided by invoking the method
 *    {@code Math.toIntExact)Long} on that provided {@code Long}.
 * @throws NullPointerException Thrown if the provided long
 *    representation is {@code null}.
 * @throws ArithmeticException Thrown if the provided {@code Long}
 *    cannot be represented as an integer without overflow.
 */
public static void writeIntExact(final Long longRepresentation)
{
   out.print(longRepresentation + " => Math.toIntExact(Long) = ");
   try
   {
      out.println(Math.toIntExact(longRepresentation));
   }
   catch (Exception exception)
   {
      out.println("ERROR: " + exception);
   }
}

When the above code is executed with the range of Longs constructed in the earlier code listing (full code available on GitHub), the output looks like this:

2147483642 =>       Long.intValue() = 2147483642
2147483642 => Math.toIntExact(Long) = 2147483642
2147483643 =>       Long.intValue() = 2147483643
2147483643 => Math.toIntExact(Long) = 2147483643
2147483644 =>       Long.intValue() = 2147483644
2147483644 => Math.toIntExact(Long) = 2147483644
2147483645 =>       Long.intValue() = 2147483645
2147483645 => Math.toIntExact(Long) = 2147483645
2147483646 =>       Long.intValue() = 2147483646
2147483646 => Math.toIntExact(Long) = 2147483646
2147483647 =>       Long.intValue() = 2147483647
2147483647 => Math.toIntExact(Long) = 2147483647
2147483648 =>       Long.intValue() = -2147483648
2147483648 => Math.toIntExact(Long) = ERROR: java.lang.ArithmeticException: integer overflow
2147483649 =>       Long.intValue() = -2147483647
2147483649 => Math.toIntExact(Long) = ERROR: java.lang.ArithmeticException: integer overflow
2147483650 =>       Long.intValue() = -2147483646
2147483650 => Math.toIntExact(Long) = ERROR: java.lang.ArithmeticException: integer overflow
2147483651 =>       Long.intValue() = -2147483645
2147483651 => Math.toIntExact(Long) = ERROR: java.lang.ArithmeticException: integer overflow

The highlighted rows indicate the code processing a Long with value equal to Integer.MAX_VALUE. After that, the Long representing one more than Integer.MAX_VALUE is shown with the results of attempting to convert that Long to an int using Long.intValue() and Math.toIntExact(Long). The Long.intValue() approach encounters an integer overflow, but does not throw an exception and instead returns the negative number -2147483648. The Math.toIntExact(Long) method does not return a value upon integer overflow and instead throws an ArithmeticException with the informative message "integer overflow."

The Math.toIntExact(Long) method is not as significant as many of the features introduced with JDK 8, but it can be useful in avoiding the types of errors related to integer overflow that can sometimes be tricky to diagnose.

Monday, June 11, 2018

Peeking Inside Java Streams with Stream.peek

For a Java developer new to JDK 8-introduced pipelines and streams, the peek(Consumer) method provided by the Stream interface can be a useful tool to help visualize how streams operations behave. Even Java developers who are more familiar with Java streams and aggregation operations may occasionally find Stream.peek(Consumer) useful for understanding the implications and interactions of complex intermediate stream operations.

The Stream.peek(Consumer) method expects a Consumer, which is essentially a block of code that accepts a single argument and returns nothing. The peek(Consumer) method returns the same elements of the stream that were passed to it, so there will be no changes to the contents of the stream unless the block of code passed to the peek(Consumer) method mutates the objects in the stream. It's likely that the vast majority of the uses of Stream.peek(Consumer) are read-only printing of the contents of the objects in the stream at the time of invocation of that method.

The Javadoc-based API documentation for Stream.peek(Consumer) explains this method's behaviors in some detail and provides an example of its usage. That example is slightly adapted in the following code listing:

final List<String> strings
   = Stream.of("one", "two", "three", "four")
      .peek(e-> out.println("Original Element: " + e))
      .filter(e -> e.length() > 3)
      .peek(e -> out.println("Filtered value: " + e))
      .map(String::toUpperCase)
      .peek(e -> out.println("Mapped value: " + e))
      .collect(Collectors.toList());
out.println("Final Results: " + strings);

When the above code is executed, its associated output looks something like this:

Original Element: one
Original Element: two
Original Element: three
Filtered value: three
Mapped value: THREE
Original Element: four
Filtered value: four
Mapped value: FOUR
Final Results: [THREE, FOUR]

The output tells the story of the stream operations' work on the elements provided to them. The first invocation of the intermediate peek operation will write each element in the original stream out to system output with the prefix "Original Element:". Instances of the intermediate peek operation that occur later are not executed for every original String because each of these peek operations occur after at least once filtering has taken place.

The peek-enabled output also clearly shows the results of executing the intermediate operation map on each String element to its upper case equivalent. The collect operation is a terminating operation and so no peek is placed after that. Strategic placement of peek operations provides significant insight into the stream processing that takes place.

The Javadoc for Stream.peek(Consumer) states that "this method exists mainly to support debugging, where you want to see the elements as they flow past a certain point in a pipeline." This is exactly what the example and output shown above demonstrate and is likely the most common application of Stream.peek(Consumer).

Stream.peek(Consumer)'s Javadoc documentation starts with this descriptive sentence, "Returns a stream consisting of the elements of this stream, additionally performing the provided action on each element as elements are consumed from the resulting stream." In the previous example, the action performed on each element as it was consumed was to merely write its string representation to standard output. However, the action taken can be anything that can be specified as a Consumer (any code block accepting a single argument and returning no arguments). The next example demonstrates how peek(Consumer) can even be used to change contents of objects on the stream.

In the first example in this post, peek(Consumer) could not change the stream elements because those elements were Java Strings, which are immutable. However, if the stream elements are mutable, the Consumer passed to peek(Consumer) can alter the contents of those elements. To illustrate this, I'll use the simple class MutablePerson shown next.

MutablePerson.java

package dustin.examples.jdk8.streams;

/**
 * Represents person whose name can be changed.
 */
public class MutablePerson
{
   private String name;

   public MutablePerson(final String newName)
   {
      name = newName;
   }

   public String getName()
   {
      return name;
   }

   public void setName(final String newName)
   {
      name = newName;
   }

   @Override
   public String toString()
   {
      return name;
   }
}

The next code listing shows how Stream.peek(Consumer) can change the results of the stream operation when the elements in that stream are mutable.

final List<MutablePerson> people
   = Stream.of(
      new MutablePerson("Fred"),
      new MutablePerson("Wilma"),
      new MutablePerson("Barney"),
      new MutablePerson("Betty"))
   .peek(person -> out.println(person))
   .peek(person -> person.setName(person.getName().toUpperCase()))
   .collect(Collectors.toList());
out.println("People: " + people);

When the above code is executed, it produces output that looks like this:

Fred
Wilma
Barney
Betty
People: [FRED, WILMA, BARNEY, BETTY]

This example shows that the Consumer passed to peek did change the case of the peoples' names to all uppercase. This was only possible because the objects being processed are mutable. Some have argued that using peek to mutate the elements in a stream might be an antipattern and I find myself uncomfortable with this approach (but I also generally don't like having methods' arguments be "output parameters"). The name of the peek method advertises one's just looking (and not touching), but the Consumer argument it accepts advertises that something could be changed (Consumer's Javadoc states, "Unlike most other functional interfaces, Consumer is expected to operate via side-effects"). The blog post "Idiomatic Peeking with Java Stream API" discusses potential issues associated with using Stream.peek(Consumer) with mutating operations.

Steam.peek(Consumer) is a useful tool for understanding how stream operations are impacting elements.

Tuesday, May 29, 2018

Deferred Execution with Java's Supplier

In the third chapter of his 2014 book "Java SE 8 for the Really Impatient: Programming with Lambdas," Cay Horstmann writes, "The point of all lambdas is deferred execution." He adds, "After all, if you wanted to execute some code right now, you'd do that, without wrapping it inside a lambda." Horstmann then lists several examples of the "many reasons for executing code later" and his last listed reason is "Running the code only when necessary." In this post, I look at some examples of this from the JDK that use Supplier to do exactly that: to execute a "code block" represented as a lambda expression "only when necessary."

When JDK 8 introduced lambda expressions, it also introduced several standard functional interfaces in its java.util.function package. That package's Javadoc documentation states, "Functional interfaces provide target types for lambda expressions and method references." The package contains standard functional interfaces such as Predicate (accepts single argument and represents boolean), Function (accepts single argument and provides single result), Consumer (accepts single argument and does not provide a result), and Supplier (accepts no arguments and represents/supplies single result). [As a side note, the blog post "Java 8 Friday: The Dark Side of Java 8" provides a highly useful "decision tree to [determine] ... the [standard functional interface] you're looking for" when trying to determine which standard functional interface to use (or to roll your own).]

In this post, I'll be focusing on JDK uses of Supplier, implying that the examples covered here are based on JDK-provided methods whose "target types" accept no arguments and supply a single result. These examples will only invoke the single get() method associated with the provided Supplier "when necessary." Note that all JDK uses of Supplier can be found in Supplier's Javadoc-generated HTML representation by clicking on the "Use" link.

Deferring Potential Expensive Operations Until Known to Be Necessary to Log Them

A nice benefit achieved with Supplier-provided deferred execution (sometimes also called "lazy evaluation") is in deferring of an expensive operation for generating a log message until it is known that the result of that expensive operation will actually be logged. In other words, the expensive operation will only be invoked when it is known that it is necessary to do so because the result will be logged. I have blogged about this before in the posts "Java 8 Deferred Invocation with Java Util Logging" and "Better Performing Non-Logging Logger Calls in Log4j2."

Before the modern Java logging libraries and frameworks featured these Supplier-accepting APIs, the common way to avoid incurring expensive operations whose results would not be logged anyway was to use API-based "guard" statements, but these can clutter the code and reduce readability. There are now numerous resources on how to use Supplier-based logging framework APIs to ensure that an expensive operation that might potentially be logged is not actually executed until it is known to be necessary. These resources include my two just-mentioned posts on java.util.logging and Log4j 2 as well as "Writing clean logging code using Java 8 lambdas," "Lazy logging in Java 8," and "Log4j 2 and Lambda Expressions."

The JDK supports this Supplier-powered deferred execution for logging in both its java.lang.SystemLogger "log" methods and in numerous java.util.logging.Logger methods.

Deferring Alternative Calculation for Optional Until Known to Be Necessary

The JDK 8-introduced Optional class provides several methods that accept a Supplier parameter. In all cases, the intent is that the alternative result that the Supplier can provide will only be calculated when it's known to be necessary to calculate it. In other words, the Supplier.get() is only invoked if the Optional is "empty." If Optional is not empty, all of these methods will return the value held in the Optional and won't invoke the Supplier potentially costly operation in those cases. The following list shows the methods on Optional that accept a Supplier as of JDK 10:

Deferring Handling of Undesired null Values Until Known to Be Necessary

JDK 8 added two Supplier-powered methods to the Objects class that allow for an unwanted null (typically on a method parameter) to be detected and an appropriate response supplied only when determined that a null was indeed encountered. I wrote about one of these methods, Objects.requireNonNullElseGet, in my blog post "JDK 9: NotNullOrElse Methods Added to Objects Class." This method will only execute the provided Supplier by calling its get() method when it is determined that that first parameter passed to the method is indeed null. If the first parameter is not null, the Supplier is never executed.

The second Supplier-accepting method added to Objects with JDK 9 is requireNonNull​(T obj, Supplier<String>). This method allows a Supplier to supply a String to be used in a "customized NullPointerException" thrown when the first parameter is null. If the first parameter is not null, the Supplier's get() never needs to be invoked because an exception does not need to be thrown. A very similar method exists on the Objects class that accepts a String instead of a Supplier. This requireNonNull​(T, String) method may be preferable in some cases where the cost of the String generation is deemed less than the cost of generating the supplier. If, for example, the "custom string" is a string literal, it might be preferable to use the form that accepts that String directly instead of the one expecting a Supplier, especially if the null case is expected to be common.

Other JDK Uses of Supplier for Deferred Execution

The JDK examples of using Supplier to defer execution shown in this post were selected because of their relatively frequent use and because they are straightforward examples of how Supplier enables deferred execution or lazy evaluation in cases where no argument needs to be provided and one result needs to be supplied. Other uses of Supplier for deferred execution can be found in the java.util.concurrent concurrency package and in the JDK 8 streams-supporting java.util.streams package. Perhaps the best way to quickly identify the JDK's use of Supplier and its primitive-oriented counterparts is to look at the Javadoc-generated HTML representation of each classes "uses": Uses of Supplier, Uses of DoubleSupplier, Uses of IntSupplier, and Uses of LongSupplier. Interestingly, but not too surprisingly, there are no uses of BooleanSupplier.

The JDK-provided examples of using Supplier discussed in this post mostly demonstrate Horstmann's last listed possible reason for deferring code execution to a later point: "Running the code only when necessary." Other JDK examples of using Supplier demonstrate some of Horstmann's other listed reasons that one might want to "[execute] code later."

Suppliers Are Not Limited to the JDK

Although this post has focused on JDK use of Supplier to defer execution, its use is not limited to the JDK. As discussed earlier, Log4j 2 and other logging frameworks besides java.util.logging also provide Supplier-based logging APIs to allow deferral of construction of expensive strings for logging until it's known that the string will actually be logged. Similarly, one can use Suppliers in one's own code whenever the need arrives to specify code to be executed only when necessary and that code to be potentially exercised does not require an argument passed to it and will return (supply) a single result.

Monday, February 5, 2018

Java 8: Bastion of Long-term Support

Stephen Colebourne's post "Java 9 has six weeks to live" starts, "Java 9 is obsolete in just six weeks." Colebourne references the Mark Reinhold blog post "Moving Java Forward Faster" and writes, "The new Java release train means that there will be a new release of Java every six months. And when the next release comes out, the previous release is obsolete." Colebourne points out that those still on Java 8 can enjoy this "current LTS (long term support) release until the next LTS release occurs (Java 11)." However, for those who have already moved to Java 9, different choices must be made and Colebourne outlines these choices at a high level. Colebourne outlines several types of dependencies that must also move forward every six months and concludes, "I think it's fair to say that it's a bold choice to use Java 9 or 10."

As a reminder, the aforementioned Reinhold blog post "Moving Java Forward Faster" outlines how the new proposed release train addresses "the tension between developers, who prefer rapid innovation, and enterprises, which prefer stability, and the fact that everyone prefers regular and predictable releases." The following are key points of this new release train approach:

  • "Adopt a strict, time-based model with a new feature release every six months, update releases every quarter, and a long-term support release every three years." (I added the emphasis)
  • Feature Releases (which "contain any type of feature") ship in March and September of each year with the first one being March 2018 (JDK 10 that Colebourne references when he writes, "Java 9 has six weeks to live").
  • Update Releases (which are "strictly limited to fixes of security issues, regressions, and bugs in newer features") occur between the Feature Releases with two Update Releases between each Feature Release and scheduled with quarter periodicity in the months of January, April, July, and October.
  • Long-Term Support Releases are the same as the Feature Release every third year starting in September 2018. Updates for these long-term support releases will be available at least until the next long-term support release and often may be available longer than those three years.
  • Additional details regarding the Java release train can be found at the #javatrain Twitter handle, in the General OpenJDK Discussion distribution list, in the page "Oracle Java SE Support Roadmap," and in the page "Faster and Easier Use and Redistribution of Java SE."
  • It was recently announced that "the public availability of Java SE 8 updates from Oracle has been extended to at least January 2019" and that "Oracle will continue to provide consumers with updates for personal (non-corporate) use of Java SE 8 through at least the end of 2020."

Colebourne is not the only one to warn Java developers to consider the ramifications of moving from Java 8 to Java 9. In the post "Java 9: No Long-Term Support and Twice-Yearly JDK Releases," Carly Yuk writes that "Java 9 will not be entitled to long-term maintenance." Yuk adds that "enterprises that are running applications in products may want to consider waiting for the future long-term release." Paul Krill writes that "Java 9 will not receive long-term support" and Ben Evans has been paraphrased, "Since Oracle has announced that Java 8 will be a long-term support release, supported through 2022, Evans thinks that a lot of applications might stay on Java 8 and not upgrade to Java 9 at all."

There is much to think about when deciding whether to upgrade to Java 9 or not. There is no single "correct" answer as situations, environments, priorities, and uses of Java differ greatly. In general, developers of larger "enterprise" type applications will likely want to only adopt the long-term support releases and developers of smaller applications will likely be willing to adopt feature releases and associated update releases to get access to new features sooner. This ability to choose between "rapid innovation" and supported stable versions is one of the driving motivations for the new release train.

Monday, January 29, 2018

Considerations When Returning Java 8's Optional from a Method

The Optional class introduced with Java 8 has been one of the most controversial features introduced by that version of the language. Although I like more things about this new Java class than I dislike, there are a few things to consider when employing it as a return type in Java methods. I discuss some of them in this post, but don't discuss the controversy about whether Optional should be limited to use as a return type. I also assume that Optional is only used as a return type when it's expected that there are cases where that method should have no value to return. Finally, these observations apply to other types and to direct use of null in Java as well, but Optional emphasizes and concretely illustrates these observations.

Single Versus Multiple Returns

There has been a debate ("religious war") in the general software development community and in the Java development community specifically for some time about whether methods should be written to only return once (not counting throwing exceptions in this discussion). On one side, Yegor Bugayenko argues that "many return statements are a bad idea in OOP", Tom Dalling argues that "having one exit point (return) from a function is a good thing", and many have argued that multiple return statements often indicate need of that method to be refactored. On the other side, Bruce Eckel argues that multiple return statements can make the code "clearer", Taylor Gautier argues that the "maxim" "A method should have one and only one exit point" "couldn't be more wrong", Peter Ritchie argues that strict adherence to single-exit can lead to "less readable" code today in "in Object-Oriented languages", and Mark Levison outlines "some reasons I dislike the single exit argument."

In the post "Multiple Return Statements," Nicolai Parlog writes about the history of and considerations to be made related to the idea of a method returning only once. He includes a section "Situations For Multiple Returns Statements" in which he outlines "several kinds of situations in which a method can profit from multiple return statements." My best guess is that many developers feel the way I do, which is that "it depends" when deciding whether a particular method should have only one return statement or should have more than one return statement.

As I've begun using Java 8's Optional more frequently for my methods' return types, I'm finding that the use of Optional as a return type is another consideration to be made when deciding whether to return once or multiple times from a method.

When declaring that a Java method returns an Optional it's important to fully understand that this does not preclude the developer who writes this method from returning null. The returned Optional is a reference type and, like any reference type, can be null. It is paramount that a developer writing a method that returns Optional should NEVER have that method return null [Optional.empty() should generally be returned instead]. I'm going to reiterate this point with two quotations:

  • Highlighted sentence from Item #55 in Effective Java, Third Edition: "Never return a null value from an Optional-returning method."
  • Stuart Marks's #1 rule for using Optional, "Never, ever, use null for an Optional variable or return value."

One of the arguments against multiple return statements in a method is that it makes it more difficult to recognize what is returned in each case (to find all the different possible return scenarios). The use of Optional as a return type is a specific example illustrating this. One would want to make sure that one's method does not return null in some cases and an Optional instance in other cases. The compiler certainly won't care which is returned in each case.

One approach for dealing with this is to only return from a method once and then the developer writing the code and the developer reviewing the code can easily ensure that null is not returned. These developers would need to only look for an Optional.of(T) call, an Optional.ofNullable(T) call, or an Optional.empty() call.

Work with Local Variable of Underlying Data Type in Method

This approach at avoiding the accidental return of null instead of an empty Optional works best when the Optional is instantiated at the point of return. In other words, I have found it better to work with the type wrapped by the Optional throughout the method and then place it in the Optional at the last possible moment. The next code listing provides ridiculously trivial examples of this.

Examples of Declaring Local Variable that is Ultimately Returned as Optional

/**
 * Provides the middle name if it exists.
 *
 * @return Middle name if it exists or empty if it doesn't exist.
 */
public Optional<String> determineMiddleName1()
{
   String middleName;
   // Do whatever logic is necessary
   return Optional.ofNullable(middleName);
}

/**
 * Provides the middle name if it exists.
 *
 * @return Middle name if it exists or empty if it doesn't exist.
 */
public Optional<String> determineMiddleName2()
{
   Optional<String> middleName;
   // Do whatever logic is necessary
   return middleName;
}

In the above code, the determineMiddleName1() method works with the local variable of the underlying type. This is typically easier to set/populate than Optional and the use of Optional.isNullable() at the ends ensures that even null middleName will be returned as an "empty" Optional instead of null.

The determineMiddleName2() method in the above code declares its local variable that will ultimately be returned as an Optional<String> and then returns that reference at the end of the method.

Avoid "Default" Initialization of Local Variable

As method determineMiddleName2() is written above, the compiler will help ensure that the local variable "middleName" is set to something (even if that "something" is null), but had the developer chosen to initialize that "middleName" variable to null to begin with, the compiler would have no issue with it. If the local variable needs to be initialized for some reason, it would be better to initialize it to Optional.empty() instead of null. Had the developer chosen to initialize that variable with Optional.empty(), it's still possible for that second example to "reset" the local variable to null later in the method.

This discussion leads me to three opinionated observations regarding use of Optional as a method return type in Java.

  1. The influence of single return or multiple returns on the possibility of returning null accidentally instead of an "empty" or other non-null Optional reference should be considered when deciding on whether a single return or multiple returns makes most sense.
  2. It will often be easier to ensure return of an Optional reference rather than return of a null by working on the the underlying type throughout the method and only instantiating the returned Optional at the latest moment (typically at its return).
  3. A local variable of type Optional that is ultimately to be returned from a method should NEVER be initially assigned to null even if it is "known" that it will be re-set appropriately. It may be best to not define it at all so that the compiler will ensure it needs to be set in each "branch" of the code flow. At the very least, if it is to be initialized, that local variable of type Optional should be initialized to Optional.empty() instead of to null.

These observations can be combined. For example, when it's determined that a method should have multiple returns (such as to implement guard clauses), one could return appropriate non-null Optional references at the point of each return and not initialize the local variable until needed (after getting through the guards). This is illustrated with the next ridiculously contrived example.

Example of Avoid Return of Null with Multiple Return Statements

public Optional<String> getGuardedData(final String input)
{
   if (input == null)
   {
      return Optional.empty();
   }

   String data;
   // Do whatever logic is necessary
   return Optional.ofNullable(data);
}

I have found the Optional class, when used properly as a method return type, can make the client's code more readable because of its greater fluency. However, to achieve its maximum value, Optional must be applied with discipline such that clients of the code can expect the returned Optional to never be null. This post has looked at a few considerations that can be made to help ensure that null is never returned from a method that advertises itself as returning Optional. Without trust that the method will never return null, use of Optional as a return type only makes things worse because it forces the client to first check for a non-null Optional before invoking one of the methods on that Optional. That makes the calling code less fluent.