Thursday, December 31, 2020

Java Predictions for 2021 on Foojay

Geertjan Wielenga has posted "Java Predictions for 2021" on Foojay Today. It is a collection of predictions about Java in 2021 from eight members of the Java community (Almas Baimagambetov, Stephen Chin, Brice Dutheil, Marcus Hirt, Reza Rahman, Matt Raible, Simon Ritter, and me). The predictions are concisely written and it's interesting to see the overlap between them while at the same time seeing how different parts of "Java" are important to different people. In this post, I elaborate a bit more on my predictions that were included in "Java Predictions for 2021".

I provided two somewhat related predictions for Java in 2021:

  • "Records will likely be finalized in 2021 and will be widely popular with Java developers who are fortunate enough to work on a version of the JDK with final (not preview) Record support.
  • The release of the OpenJDK 17 implementation in 2021 (which will be the foundation of Oracle's LTS version and other community members' LTS versions) will motivate many who are already working on versions of the JDK later than JDK 8 to start moving or investigate moving to JDK 17. However, JDK 8 will remain widely popular (probably will still be used by over half of Java developers), creating (in the long term) a bimodal distribution of most commonly used JDK versions (8 and 17)."

Java Records Final in 2021

The prediction that Java Records will be final in 2021 is not a risky one. Records have a been a "preview" feature in JDK 14 (JEP 359) and JDK 15 (JEP 384) and now JEP 395 "proposes to finalize the feature in JDK 16" (which is currently in Rampdown Phase One and is scheduled to be released for General Availability in March 2021). Because Records have been through two preview releases already, it seems unlikely that they won't be final as of JDK 16. In the event that they do need one more release, JDK 17 should be released in October 2021.

And Then There Were Two: JDK 8 and JDK 17

2021 will see the beginning of a move to a bimodial distribution of JDK releases most commnoly used. With JDK 17's likely release in October 2021, we'll likely see many Java shops that have already migrated to a version of JDK later than JDK 8 move to that newly released JDK 17. There have been some nice additions and improvements to OpenJDK (which is the foundation of several different JDK implementations) that have been added in recent versions of the JDK and JDK 17 will be an "LTS" (Long-term Support) release for many of the JDK implementations. As an "LTS" release, JDK 17 will appeal to Java shops that want to only be on versions with long-term support and JDK 17 will be the first since JDK 11 to have this status for many of the JDK implementations.

JDK 8 appears to still be the most widely used release of the Java even in 2020. There are several metrics and andecdotal evidence that suggest this. One example is the JetBrains 2020 Development Ecosystem survey that suggests that 75% of Java developers responding to the survey use JDK 8 (some of these developers use other versions of JDK as well) and the same chart shows 32% of responding Java developers use JDK 11. For reference, the 2019 and 2018 versions of this same survey indicated that 83% and 84% of Java developers used JDK 8 in 2019 and 2018 respectively.

JDK 8 is a version with long-term support (Oracle, for example, offers "extended support" for JDK 8 through December 2030) in several JDK implementations and some shops appear hesitant to move to JDK 9 with its introduced modularity support (and need for libraries and frameworks to support that as well). For those shops that have already migrated to a version of JDK later than JDK 8, it should be relatively easier to migrate to JDK 17. I think that some JDK 8 shops will be motivated to make the "big move" and, while doing that, will jump directly to JDK 17. However, I expect that we'll still see at least half of JDK developers still using JDK 8 even at the end of 2021. For the half of JDK users already using a version later than JDK 8 (not counting users of version of JDK before JDK 8), I think we'll begin to see them migrate to JDK 17 in 2021 and the following year or two. Within the next year or two, I expect most JDK developers will be working with JDK 8 or JDK 17.

There will, of course, be some small pockets of JDK developers using other versions before JDK 8, between JDK 8 and JDK 17 (perhaps because they use a feature or garbage collector no longer available in JDK 17), and newer versions of JDK as they are released in 2022.

"LTS" Among JDK Providers

The following are some roadmaps of various JDK vendors' JDK implementations that provide insight into each vendor's LTS concept. Although "LTS" often is referring to Oracle's plan regarding their JDK implementation built on top of OpenJDK, other JDK vendors have generally treated these "LTS" releases in similar manner.

  • AdoptOpenJDK Support and Release Roadmap
    • Shows "Java 17" as LTS.
    • States, "In addition, every three years one feature release will be designated as a Long Term Supported (LTS) release. We will produce LTS releases for at least four years."
    • States, "As a general philosophy, AdoptOpenJDK will continue to build binaries for LTS releases as long as the corresponding upstream source is actively maintained."
  • Oracle Java SE Support Roadmap
    • States, "For product releases after Java SE 8, Oracle will designate a release, every three years, as a Long-Term-Support (LTS) release. Java SE 11 is an LTS release."
  • Azul Java Support Roadmap
    • References Long Term Support (LTS) and Medium Term Support (MTS) and states, "Releases designated as LTS are those same LTS releases as designated by Oracle and the OpenJDK community."
  • Amazon Corretto
    • "Amazon Corretto 8 & 11 support extended" states, "Amazon is extending long-term support (LTS) for Amazon Corretto 8 from June 2023 to May 2026 and for Amazon Corretto 11 from August 2024 to September 2027. Long-term support (LTS) for Corretto includes security updates and specific performance enhancements released at least quarterly."

Looking Forward to 2021

Most of us are hoping for a better year in 2021 than we've experienced in 2020. The finalization of Java Records and General Availability of JDK 17 in 2021 are going to be significant positive events for Java developers and I'm hoping that these will only be a small representative sample of positive events and advancements that benefit a much wider population in 2021.

Monday, December 7, 2020

JDK 16: Stream to List In One Easy Call

As Java functional streams have become increasingly popular, an increasing number of requests is being made for new stream operations to be supported. Amidst these requests for numerous disparate new operations, one operation that seems to be requested more than the others is an operation that directly provides a List from a Stream. JDK 16 Early Access Build 27 introduces Stream.toList(), which is the subject of this post.

Before the JDK 16 Early Access Build 27 introduction of Stream.toList(), the most common approach for acquiring a List from a Stream was to invoke the approprite Collector:

stream.collect(Collectors.toList())

This is not a lot of code and it's fairly straightforward once you see it, but many have wanted an even more concise syntax for this frequently used stream operation. JDK 16 brings us this:

stream.toList()

It may be tempting to go into one's code base and use stream.toList() as a drop-in replacement for stream.collect(Collectors.toList()), but there may be differences in behavior if the code has a direct or indirect dependency on the implementation of stream.collect(Collectors.toList()) returning an ArrayList. Some of the key differences between the List returned by stream.collect(Collectors.toList()) and stream.toList() are spelled out in the remainder of this post.

The Javadoc-based documentation for Collectors.toList() states (emphasis added), "Returns a Collector that accumulates the input elements into a new List. There are no guarantees on the type, mutability, serializability, or thread-safety of the List returned..." Although there are no guarantees regarding the "type, mutability, serializability, or thread-safety" on the List provided by Collectors.toList(), it is expected that some may have realized it's currently an ArrayList and have used it in ways that depend on the characteristics of an ArrayList.

The following code snippet (full code listing on GitHub) shows a method that can be executed against the List implementations returned by Collectors.toList() and Stream.toList() to see what they have in common and how the are different.

/**
 * Analyzes the supplied {@code List} and writes to standard output
 * some key characteristics of the supplied {@code List}.
 *
 * @param listDescription Description of {@code List} to be analyzed.
 * @param listUnderAnalysis {@code List} to be analyzed.
 */
private static void analyzeList(
   final String listDescription, final List<String> listUnderAnalysis)
{
   out.println(listDescription + ": ");
   out.println("\tClass Type: " + listUnderAnalysis.getClass().getCanonicalName());
   out.println("\tAble to add to List? " + isListAddCapable(listUnderAnalysis));
   out.println("\tAble to sort List?   " + isListSortable(listUnderAnalysis));
}

When the simple analysis code above is executed against implementations of List returned by Stream.collect(Collectors.toList()) and Stream.toList(), the output appears as shown next.

Stream.collect(Collectors.toList()): 
	Class Type: java.util.ArrayList
	Able to add to List? true
	Able to sort List?   true
Stream.toList(): 
	Class Type: java.util.ImmutableCollections.ListN
	Able to add to List? false
	Able to sort List?   false
[NOT Stream] List.of(): 
	Class Type: java.util.ImmutableCollections.ListN
	Able to add to List? false
	Able to sort List?   false

The output shown above demonstrates that Stream.toList() provides a List implementation that is immutable (type ImmutableCollections.ListN that cannot be added to or sorted) similar to that provided by List.of() and in contrast to the mutable (can be changed and sorted) ArrayList provided by Stream.collect(Collectors.toList()). Any existing code depending on the ability to mutate the ArrayList returned by Stream.collect(Collectors.toList()) will not work with Stream.toList() and an UnsupportedOperationException will be thrown.

Although the implementation nature of the Lists returned by Stream.collect(Collectors.toList()) and Stream.toList() are very different, they still both implement the List interface and so they are considered equal when compared using List.equals(Object). This is demonstrated in the full code listing on GitHub.

The addition of method toList() to the Stream interface is a small thing, but it does make an often-used technique more convenient.

Saturday, December 5, 2020

Java's String.repeat Method in Action: Building PreparedStatement with Dynamic Number of Parameters

Java's String.repeat(int) method is an example of a "small" addition to Java (introduced with JDK 11) that I find myself frequently using and appreciating. This post describes use of JDK 11-introduced String.repeat(int) for easier custom generation of SQL WHERE clauses with the appropriate number of "?" parameter placeholders for use with PreparedStatements.

Many Java developers do not need to manually build PreparedStatements with the approprite number of parameter placeholders because they take advantage of a JPA implementation, other ORM framework, or library that handles it for them. However, the demonstrations in this post show how String.repeat(int) can make light work of any implementation that needs to build up a string with a specified number of repeated pieces.

 

Building SQL IN Condition with Dynamic Number of Parameters

A common approach used in Java applications for building a custom SQL SELECT statement that queries a particular database column against a collection of potential values is to use the IN operator and pass all potential matching values to that IN operator.

One Java implementation approach for building the IN operator portion of the SQL SELECT's WHERE clause is to iterate the same number of times as there are parameters for the IN operator and to use a conditional within that loop to determine the how to properly add that portion of the in-progress IN portion. This is demonstrated in the next code listing:

/**
 * Demonstrates "traditional" approach for building up the
 * "IN" portion of a SQL statement with multiple parameters
 * that uses a conditional within a loop on the number of
 * parameters to determine how to best handle each.
 *
 * @param columnName Name of database column to be referenced
 *    in the "IN" clause.
 * @param numberPlaceholders Number of parameters for which
 *    placeholder question marks ("?") need to be added.
 * @return The "IN" portion of a SQL statement with the
 *    appropriate number of placeholder question marks.
 */
public String generateInClauseTraditionallyOne(
   final String columnName, final int numberPlaceholders)
{
   final StringBuilder inClause = new StringBuilder();
   inClause.append(columnName + " IN (");
   for (int placeholderIndex = 0; placeholderIndex < numberPlaceholders; placeholderIndex++)
   {
      if (placeholderIndex != numberPlaceholders-1)
      {
         inClause.append("?, ");
      }
      else
      {
         inClause.append("?");
      }
   }
   inClause.append(")");
   return inClause.toString();
}

A second traditional approach for building up the IN clause to use a dynamic number of parameter placeholders is to again loop the same number of times as there are parameters, but append exactly the same new text each iteration. After iteration is completed, the extra characters are chopped off the end. This approach is shown in the next code listing:

/**
 * Demonstrates "traditional" approach for building up the
 * "IN" portion of a SQL statement with multiple parameters
 * that treats each looped-over parameter index the same and
 * the removes the extraneous syntax from the end of the
 * generated string.
 *
 * @param columnName Name of database column to be referenced
 *    in the "IN" clause.
 * @param numberPlaceholders Number of parameters for which
 *    placeholder question marks ("?") need to be added.
 * @return The "IN" portion of a SQL statement with the
 *    appropriate number of placeholder question marks.
 */
public String generateInClauseTraditionallyTwo(
   final String columnName, final int numberPlaceholders)
{
   final StringBuilder inClause = new StringBuilder();
   inClause.append(columnName + " IN (");
   for (int placeholderIndex = 0; placeholderIndex < numberPlaceholders; placeholderIndex++)
   {
      inClause.append("?, ");
   }
   inClause.delete(inClause.length()-2, inClause.length());
   inClause.append(")");
   return inClause.toString();
}

JDK 11 introduced a set of useful new String methods that include String.repeat(int). The String.repeat(int) method boils these approaches for generating a custom IN operator with dynamic number of parameter placeholders to a single line as shown in the next code listing:

/**
 * Demonstrates JDK 11 {@link String#repeat(int)} approach
 * for building up the "IN" portion of a SQL statement with
 * multiple parameters.
 *
 * @param columnName Name of database column to be referenced
 *    in the "IN" clause.
 * @param numberPlaceholders Number of parameters for which
 *    placeholder question marks ("?") need to be added.
 * @return The "IN" portion of a SQL statement with the
 *    appropriate number of placeholder question marks.
 */
public String generateInClauseWithStringRepeat(
   final String columnName, final int numberPlaceholders)
{
   return columnName + " IN (" + "?, ".repeat(numberPlaceholders-1) + "?)";
}

With the use of String.repeat(int), a single line accomplishes the task at hand and there's no need for explicit looping or explicit instantiation of a StringBuilder.

 

Building SQL OR Conditions with Dynamic Number of Parameters

Multiple SQL or conditions can be used instead of IN to test against multiple values. This is a must if, for example, the number of paramaters is over 1000 and you're using an Oracle database that only allows IN to support up to 1000 elements.

As with use of the IN condition, two commonly used approaches for building up the OR conditions for a dynamic number of parameter placeholders are to either to loop with a condition checking that each entry's output is written correctly as it's written or to remove extraneous characters after looping. These two approaches are shown in the next code listing:

/**
 * Demonstrates "traditional" approach for building up the
 * "OR" portions of a SQL statement with multiple parameters
 * that uses a conditional within a loop on the number of
 * parameters to determine how to best handle each.
 *
 * @param columnName Name of database column to be referenced
 *    in the "OR" clauses.
 * @param numberPlaceholders Number of parameters for which
 *    placeholder question marks ("?") need to be added.
 * @return The "OR" portions of a SQL statement with the
 *    appropriate number of placeholder question marks.
 */
public String generateOrClausesTraditionallyOne(
   final String columnName, final int numberPlaceholders)
{
   final StringBuilder orClauses = new StringBuilder();
   for (int placeholderIndex = 0; placeholderIndex < numberPlaceholders; placeholderIndex++)
   {
      if (placeholderIndex != numberPlaceholders-1)
      {
         orClauses.append(columnName).append(" = ? OR ");
      }
      else
      {
         orClauses.append(columnName).append(" = ?");
      }
   }
   return orClauses.toString();
}

/**
 * Demonstrates "traditional" approach for building up the
 * "OR" portions of a SQL statement with multiple parameters
 * that treats each looped-over parameter index the same and
 * the removes the extraneous syntax from the end of the
 * generated string.
 *
 * @param columnName Name of database column to be referenced
 *    in the "OR" clauses.
 * @param numberPlaceholders Number of parameters for which
 *    placeholder question marks ("?") need to be added.
 * @return The "OR" portions of a SQL statement with the
 *    appropriate number of placeholder question marks.
 */
public String generateOrClausesTraditionallyTwo(
   final String columnName, final int numberPlaceholders)
{
   final StringBuilder orClauses = new StringBuilder();
   for (int placeholderIndex = 0; placeholderIndex < numberPlaceholders; placeholderIndex++)
   {
      orClauses.append(columnName + " = ? OR ");
   }
   orClauses.delete(orClauses.length()-4, orClauses.length());
   return orClauses.toString();
}

The use of String.repeat(int) makes this easy as well:

/**
 * Demonstrates JDK 11 {@link String#repeat(int)} approach
 * for building up the "OR" portions of a SQL statement with
 * multiple parameters.
 *
 * @param columnName Name of database column to be referenced
 *    in the "OR" clauses.
 * @param numberPlaceholders Number of parameters for which
 *    placeholder question marks ("?") need to be added.
 * @return The "OR" portions of a SQL statement with the
 *    appropriate number of placeholder question marks.
 */
public String generateOrClausesWithStringRepeat(
   final String columnName, final int numberPlaceholders)
{
   final String orPiece = columnName + " = ? OR ";
   return orPiece.repeat(numberPlaceholders-1) + columnName + " = ?";
}

 

Conclusion

The introduction of String.repeat(int) makes it easer for Java developers to implement custom generation of Java Strings that consist of dynamically repeated portions.

All code snippets shown in this post are available on GitHub.

Saturday, November 28, 2020

JDK 16: Checking Indexes and Ranges of Longs

In my last post, I described the day period support added with JDK 16 Early Access Build 25. That same build also added methods for checking indexes and ranges of long values, which is the subject of this post.

JDK-8255150 ("Add utility methods to check long indexes and ranges") is the Enhancement used to add utility methods for checking long indexes and ranges similar to what JDK-8135248 ("Add utility methods to check indexes and ranges") added for integers with JDK 9. JDK-8255150 states, "The goal is to add a similar set of methods [as JDK-8135248] but rather than operate on int arguments, the new methods operate on long arguments."

JDK-8255150 lists the method signatures for the three new methods being added to the Objects class (descriptions provided by JDK-8135248):

  • Checking whether an index is within bounds:
    public static long checkIndex(long index, long length)
  • Checking whether an absolute range is within bounds:
    public static long checkFromToIndex(long fromIndex, long toIndex, long length)
  • Checking whether a relative range is within bounds:
    public static long checkFromIndexSize(long fromIndex, long size, long length)

Because these new methods "mirror the int utility methods," it is useful to look at JDK-8135248 to see more historical context for the justification for the introduction of these methods. That Enhancement states, "There are numerous methods in the JDK that check if an index or an absolute/relative range is valid before accessing the contents of an array (or in general a memory region for the case of a direct java.nio.ByteBuffer). ... Such checks, while not difficult, are often easy to get wrong and optimize correctly, thus there is a risk to the integrity and security of the runtime."

JDK-8135248 also talks about possibilities for optimization, "A further desire for such methods is some or all can be made intrinsic (see JDK-8042997), thus hinting to the HotSpot runtime compiler to use unsigned comparisons and better optimize array access (via aaload/store or Unsafe) in loops (especially those that are unrolled)."

A class that demonstrates these newly added methods, LongIndexRangeChecksDemo, is available on GitHub. All examples in this class demonstrate the various checks throwing IndexOutOfBoundsExceptions to indicate that the proposed index and/or size values do not fall within the allowed range. The main(String[]) function executes all the example methods and its output is divided into described sections below.

checkIndex Example Output

The message associated with this example clearly describes the index that is out of bounds and how that index is out of bounds.

==========================
== checkIndex Exception ==
==========================
java.lang.IndexOutOfBoundsException: Index 7 out of bounds for length 5
	at java.base/jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:64)
	at java.base/jdk.internal.util.Preconditions.outOfBoundsCheckIndex(Preconditions.java:88)
	at java.base/jdk.internal.util.Preconditions.checkIndex(Preconditions.java:412)
	at java.base/java.util.Objects.checkIndex(Objects.java:435)
	at dustin.examples.jdk16.check.LongIndexRangeChecksDemo.lambda$demoCheckIndexException$0(LongIndexRangeChecksDemo.java:34)
	at dustin.examples.jdk16.check.LongIndexRangeChecksDemo.executeDemonstration(LongIndexRangeChecksDemo.java:96)
	at dustin.examples.jdk16.check.LongIndexRangeChecksDemo.demoCheckIndexException(LongIndexRangeChecksDemo.java:33)
	at dustin.examples.jdk16.check.LongIndexRangeChecksDemo.main(LongIndexRangeChecksDemo.java:115)

checkFromToIndex Example Output

The message clearly indicates that the range specified by the "from" and "to" indexes is too large for the expected length capacity. Note that the "[" opening the range description indicates "inclusive" and the ")" ending the range description indicates "exclusive".

================================
== checkFromToIndex Exception ==
================================
java.lang.IndexOutOfBoundsException: Range [2, 6) out of bounds for length 3
	at java.base/jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:64)
	at java.base/jdk.internal.util.Preconditions.outOfBoundsCheckFromToIndex(Preconditions.java:94)
	at java.base/jdk.internal.util.Preconditions.checkFromToIndex(Preconditions.java:459)
	at java.base/java.util.Objects.checkFromToIndex(Objects.java:461)
	at dustin.examples.jdk16.check.LongIndexRangeChecksDemo.lambda$demoCheckFromToIndexException$1(LongIndexRangeChecksDemo.java:48)
	at dustin.examples.jdk16.check.LongIndexRangeChecksDemo.executeDemonstration(LongIndexRangeChecksDemo.java:96)
	at dustin.examples.jdk16.check.LongIndexRangeChecksDemo.demoCheckFromToIndexException(LongIndexRangeChecksDemo.java:47)
	at dustin.examples.jdk16.check.LongIndexRangeChecksDemo.main(LongIndexRangeChecksDemo.java:116)

checkFromIndexSize Example Output

This example indicates that the range formed by a "from" index and range size is out of bounds for the specified length capacity.

==================================
== checkFromIndexSize Exception ==
==================================
java.lang.IndexOutOfBoundsException: Range [2, 2 + 6) out of bounds for length 3
	at java.base/jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:64)
	at java.base/jdk.internal.util.Preconditions.outOfBoundsCheckFromIndexSize(Preconditions.java:100)
	at java.base/jdk.internal.util.Preconditions.checkFromIndexSize(Preconditions.java:507)
	at java.base/java.util.Objects.checkFromIndexSize(Objects.java:487)
	at dustin.examples.jdk16.check.LongIndexRangeChecksDemo.lambda$demoCheckFromIndexSizeException$2(LongIndexRangeChecksDemo.java:62)
	at dustin.examples.jdk16.check.LongIndexRangeChecksDemo.executeDemonstration(LongIndexRangeChecksDemo.java:96)
	at dustin.examples.jdk16.check.LongIndexRangeChecksDemo.demoCheckFromIndexSizeException(LongIndexRangeChecksDemo.java:61)
	at dustin.examples.jdk16.check.LongIndexRangeChecksDemo.main(LongIndexRangeChecksDemo.java:117)

checkFromIndexSize Overflow Example Output

This example indicates that the range formed by a "from" index and range size are out of bounds because a numeric overflow occurred when adding the size to the initial index. This is a nice catch because an overly simplistic homegrown approach that checked that the supplied initial index and supplied size are both positive and then checked the sum of the index and size against the allowed length would be faulty logic due to the overflow possibility.

=============================================
== checkFromIndexSize (Overflow) Exception ==
=============================================
java.lang.IndexOutOfBoundsException: Range [2, 2 + 9223372036854775807) out of bounds for length 3
	at java.base/jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:64)
	at java.base/jdk.internal.util.Preconditions.outOfBoundsCheckFromIndexSize(Preconditions.java:100)
	at java.base/jdk.internal.util.Preconditions.checkFromIndexSize(Preconditions.java:507)
	at java.base/java.util.Objects.checkFromIndexSize(Objects.java:487)
	at dustin.examples.jdk16.check.LongIndexRangeChecksDemo.lambda$demoCheckFromIndexSizeExceptionOnOverflow$3(LongIndexRangeChecksDemo.java:77)
	at dustin.examples.jdk16.check.LongIndexRangeChecksDemo.executeDemonstration(LongIndexRangeChecksDemo.java:96)
	at dustin.examples.jdk16.check.LongIndexRangeChecksDemo.demoCheckFromIndexSizeExceptionOnOverflow(LongIndexRangeChecksDemo.java:76)
	at dustin.examples.jdk16.check.LongIndexRangeChecksDemo.main(LongIndexRangeChecksDemo.java:118)

Common Uses

The greatest beneficiary of these newly added long-supporting methods may be the authors, maintainers, and users of the foreign memory access API as described in this mailing list message: "We have to jump through quite a few hoops in the implementation of the foreign memory access API in order to leverage the intrinsification of int-based index checks, and even then we are not covering the cases where the numbers are larger than ints. Looking forward to being able to remove those hacks!"

A common use of these methods is likely to be as method guards for checking method parameters against expected preconditions similar to how other Objects' methods such as checkIndex(int, int), checkFromToIndex(int, int, int), checkFromIndexSize(int, int, int), requireNonNull(T), and requireNonNull(T, String) are used.

Monday, November 23, 2020

Day Period Support in JDK 16

JDK 16 Early Access Build 25 (2020/11/18) includes changes for JDK-8247781 ("Day periods support"). As stated in the JDK 16 Early Access Build 25 Release Notes ("Day period support added to java.time formats"), the new functionality "translates day periods defined in Unicode Consortium's CLDR."

In most English-language situations using a "12-hour clock", the "day periods" might be used instead of the AM (ante meridiem) and PM (post meridiem) designators. The Unicode documentation "Day Period Rule Sets" describes day period rules like this: "Each locale can have a set of day period rules, which determine the periods during a day for use in time formats like '10:00 at night', or to select statements like 'Your email arrived last night.' If locales do not have dayPeriodRules, the computation of dayPeriods falls back to AM/PM."

As with most things, it's perhaps easiest to see this new functionality via code examples and their associated output. The first example shown here is adapted from the JDK 16 Early Access Build 25 Release Notes. Note that the "B" is used in the pattern to specify that a day period is to be used.

/**
 * Writes the current day period out to standard output.
 *
 * This is based on the example included with the Release Notes
 * (https://jdk.java.net/16/release-notes).
 */
public void printCurrentDayPeriod()
{
   final String currentDayPeriodStr
      = DateTimeFormatter.ofPattern("B").format(LocalTime.now());
   out.println("Pattern 'B' (time now): \"" + currentDayPeriodStr + "\"");
}

The output for the above code sample when run in evening hours is now shown:

Pattern 'B' (time now): "in the evening"

The next code sample contains two methods, showing variations of date/time formats using the "B" format pattern letter.

/**
 * Prints representation of supplied {@link ZonedDateTime} with hour,
 * day period, and time zone details to standard output.
 *
 * @param zonedDateTime Date/time that will be written to standard output
 *    and will include hour, day period, and zone details.
 */
public void printHourDayPeriodAndZone(final ZonedDateTime zonedDateTime)
{
   final String dateTimeStr
      = DateTimeFormatter.ofPattern("hh B, zzzz").format(zonedDateTime);
   out.println("Hour/Day Period/Zone: \"" + dateTimeStr + "\"");
}

/**
 * Prints representation of supplied {@link ZonedDateTime} with hour,
 * minutes, day period, and time zone details to standard output.
 *
 * @param zonedDateTime Date/time that will be written to standard output
 *    and will include hour, minutes, day period, and zone details.
 */
public void printHourDayMinutePeriodAndZone(final ZonedDateTime zonedDateTime)
{
   final String dateTimeStr
      = DateTimeFormatter.ofPattern("K:mm B z").format(zonedDateTime);
   out.println("Hour/Minute/Day Period/Zone: \"" + dateTimeStr + "\"");
}

The following two lines of output are shown when the two methods above are executed during evening hours:

Hour/Day Period/Zone: "08 in the evening, Mountain Standard Time"
Hour/Minute/Day Period/Zone: "8:07 in the evening MST"

Because the output for the examples to this point were all executed in evening hours, the day period ("in the evening") has been the same for all examples executed above.

The next code listing iterates over each hour of the day to indicate different day period expressions for the different hours when Locale.US is specified. Note that the dates/times constructed in this example have non-zero fractional hours (non-zero minutes, seconds, and nanoseconds).

/**
 * Prints Day Period phraseology for each of 24 hours of day with
 * arbitrary minutes, seconds, and nanoseconds to standard output.
 */
public void printDayPeriodsByHour()
{
   out.println("===== Hours With Non-Zero Minutes/Seconds/Nanoseconds =====");
   final DateTimeFormatter dateTimeFormat = DateTimeFormatter.ofPattern("hh B");
   for (int hour = 0; hour < 24; hour++)
   {
      final OffsetDateTime dateTime
         = Instant.now().atOffset(ZoneOffset.UTC).withHour(hour);
      out.println("Hour " + hour + ": \"" + dateTimeFormat.format(dateTime) + "\"");
   }
}

The output from running the code above shows how different hours correspond to different "day periods" for US.Locale. Note that this output is based on a date/time with non-zero fractional hours (times are not exact hours).

===== Hours With Non-Zero Minutes/Seconds/Nanoseconds =====
Hour 0: "12 at night"
Hour 1: "01 at night"
Hour 2: "02 at night"
Hour 3: "03 at night"
Hour 4: "04 at night"
Hour 5: "05 at night"
Hour 6: "06 in the morning"
Hour 7: "07 in the morning"
Hour 8: "08 in the morning"
Hour 9: "09 in the morning"
Hour 10: "10 in the morning"
Hour 11: "11 in the morning"
Hour 12: "12 in the afternoon"
Hour 13: "01 in the afternoon"
Hour 14: "02 in the afternoon"
Hour 15: "03 in the afternoon"
Hour 16: "04 in the afternoon"
Hour 17: "05 in the afternoon"
Hour 18: "06 in the evening"
Hour 19: "07 in the evening"
Hour 20: "08 in the evening"
Hour 21: "09 at night"
Hour 22: "10 at night"
Hour 23: "11 at night"

Because the date/time used in the example above has non-zero fractional hours, the en_US day period expressions are "at night", "in the morning", "in the afternoon", and "in the evening".

The next code snippet shows a method that ensures the formatted date/time has "exact hours" (its minutes, seconds, and nanoseconds are all zero).

/**
 * Prints Day Period phraseology for each of 24 hours of day with
 * zero minutes, zero seconds, and zero nanoseconds to standard output.
 */
public void printDayPeriodsByWholeHours()
{
   out.println("===== Exact Hours =====");
   final DateTimeFormatter dateTimeFormat = DateTimeFormatter.ofPattern("hh B");
   for (int hour = 0; hour < 24; hour++)
   {
      final OffsetDateTime dateTime = OffsetDateTime.of(
         2020, 11, 23, hour, 0, 0, 0, ZoneOffset.UTC);
      out.println("Hour " + hour + ": \"" + dateTimeFormat.format(dateTime) + "\"");
   }
}

When the above code is executed, the following output is seen:

===== Exact Hours =====
Hour 0: "12 midnight"
Hour 1: "01 at night"
Hour 2: "02 at night"
Hour 3: "03 at night"
Hour 4: "04 at night"
Hour 5: "05 at night"
Hour 6: "06 in the morning"
Hour 7: "07 in the morning"
Hour 8: "08 in the morning"
Hour 9: "09 in the morning"
Hour 10: "10 in the morning"
Hour 11: "11 in the morning"
Hour 12: "12 noon"
Hour 13: "01 in the afternoon"
Hour 14: "02 in the afternoon"
Hour 15: "03 in the afternoon"
Hour 16: "04 in the afternoon"
Hour 17: "05 in the afternoon"
Hour 18: "06 in the evening"
Hour 19: "07 in the evening"
Hour 20: "08 in the evening"
Hour 21: "09 at night"
Hour 22: "10 at night"
Hour 23: "11 at night"

The output above is mostly the same as for the output associated with dates/times that had fractional hours, but the whole hours example is different for hour 0 (day period of "midnight") and hour 12 (day period of "noon").

For my last example, I'm going to use Dominican Republic/Spanish ("es DO") for the Locale with the same code just demonstrated. Here is that output:

Hour 0: "12 de la madrugada"
Hour 1: "01 de la madrugada"
Hour 2: "02 de la madrugada"
Hour 3: "03 de la madrugada"
Hour 4: "04 de la madrugada"
Hour 5: "05 de la madrugada"
Hour 6: "06 de la mañana"
Hour 7: "07 de la mañana"
Hour 8: "08 de la mañana"
Hour 9: "09 de la mañana"
Hour 10: "10 de la mañana"
Hour 11: "11 de la mañana"
Hour 12: "12 del mediodía"
Hour 13: "01 de la tarde"
Hour 14: "02 de la tarde"
Hour 15: "03 de la tarde"
Hour 16: "04 de la tarde"
Hour 17: "05 de la tarde"
Hour 18: "06 de la tarde"
Hour 19: "07 de la tarde"
Hour 20: "08 de la noche"
Hour 21: "09 de la noche"
Hour 22: "10 de la noche"
Hour 23: "11 de la noche"

Support for "day period" presentation in formatted dates/times provides Java developers with more flexibility in expressing day period details than simply using AM and PM. All code listings shown in this post are available on GitHub.

Monday, September 28, 2020

Moving Toward Inline Classes: JEP 390 and the @ValueBased Annotation

Candidate JEP 390 ("Warnings for Value-Based Classes") was announced this past week by Mark Reinhold on the OpenJDK jdk-dev mailing list. This is a JDK Enhancement Proposal that may be more exciting in what it indicates (progress toward inline classes) than what it actually is (annotation and warnings about "improper attempts to synchronize on instances" of @ValueBased-annotated JDK classes that might be come inline classes).

The term "Value-based Classes" was included in the Java SE 8 Javadoc documentation, which identified the following characteristics of "instances of a value-based class" at that time:

  • "final and immutable (though may contain references to mutable objects)"
  • Implement "equals, hashCode, and toString ... solely [computed] from the instance's state and not from its identity or the state of any other object or variable"
  • Do NOT use "identity-sensitive operations such as reference equality (==) between instances, identity hash code of instances, or synchronization on an instances's intrinsic lock"
  • "Are considered equal solely based on equals()" rather than "on reference equality (==)"
  • "Instantiated through factory methods which make no committment as to the identity of returned instances" rather than providing accessible constructors
  • "Are freely substitutable when equal"

That Java SE 8 documentation also warned that "a program may produce unpredictable results if it attempts to distinguish two references to equal values of a value-based class, whether directly via reference equality or indirectly via an appeal to synchronization, identity hashing, serialization, or any other identity-sensitive mechanism." It further emphasizes, "Use of such identity-sensitive operations on instances of value-based classes may have unpredictable effects and should be avoided." This documentation remains essentially the same today (Java 15). This documentation is linked to from certain identified JDK classes' Javadoc comments. A good overview on value-based classes is available in Nicolai Parlog's blog post "Value-Based Classes."

Although some JDK classes have been identified in their Javadoc as value-based classes since Java 8, JEP 390 aims to provide the annotation @ValueBased for these types of JDK classes. The annotation is a stronger construct than a comment and is specifically intended to be used in conjunction with "warnings about improper attempts to synchronize on instances of such classes." The advantage of such an annotation is that developers can be warned about current uses in their code to synchronize on instances of classes that may one day be unsuitable for such synchronization. Because some likely significant time will pass between the availability of this warning and the actual coversion of these @ValueBased-annotated classes to inline classes (the JEP word this as, "several releases before the migration occurs"), developers will have ample opportunity to change the classes upon which they apply synchronization.

The warning about use of JDK classes for synchronization has benefit in its own right. In addition, I think this JEP is an exciting step toward inline types and the JEP is full of details about inline types. Specifically, the first paragraph of the "Motivation" section of JEP 390 states:

The Valhalla Project is pursuing a significant enhancement to the Java programming model in the form of inline classes. Such classes declare their instances to be identity-free and capable of inline or flattened representations, where instances can be copied freely between memory locations and encoded using solely the values of the instances' fields.

Perhaps the most anticipatory sentence in JEP 390 is in the second paragraph of the "Motivation" section (I've added the highlighting): "The design and implementation of inline classes is sufficiently mature that we can confidently anticipate migrating certain classes in the Java Platform to become inline classes in a future release."

The "Motivation" section of JEP 390 also enumerates the "properties" of a "candidate inline class." These are very simlar to the characteristics of "value-based classes" listed earlier:

  • "Is final"
  • "Declares only final instance fields"
  • "Extends Object, or a hierarchy of abstract classes that declare no instance fields and have no instance initialization logic"
  • "Declares only private constructors, or has deprecated its constructors for removal"
  • "Does not promise a unique instance will be created with each factory-method invocation (or any other instance creation mechanism)"
  • "Does not rely upon or expose object identity through any of its methods"
  • "Overrides toString, equals, and hashCode"
  • "Declares no synchronized methods, and discourages clients from performing synchronization"

JEP 390 also comments on the not-so-surprising closeness of the listed "properties" of "inline classes" to the characteristics of "value-based classes":

The Java Platform API uses the term value-based class to informally describe certain classes that satisfy similar constraints. If a class is described as value-based then it will probably be able to become an inline class.

JEP 390 explains that the @ValueBased annotation "will be introduced to indicate a class, interface, or method result for which no instances are expected to rely on object identity." The annotation is intended to "communicate to developers that they should exercise caution when using == or identityHashCode, and should not perform synchronization."

One of the other sections of JEP 390 that is particularly interesting to me is the section in the "Description" that lists likely "declarations in the Java Platform API and the JDK" to which the @ValueBased annotation may be applied. These include the "primitive wrapper classes" (perhaps one of the categories of most commonly used classes with synchronization that will need to be changed), the "optional classes" such as java.util.Optional, many classes in the java.time API, the java.util "collection factories", and more.

Constructs annotated with @ValueBased annotation will be highlighted in a javac warning. JEP 390 discusses how compile-time and runtime warnings will eventully become errors in some cases as @ValueBased-annotated classes become inline classes:

When a @ValueBased class becomes an inline class, the runtime warnings will be superseded by standard errors. At compile time, synchronization on an inline class type may also trigger a standard error. Compiler warnings will continue to be useful for interface types and method results.

The @ValueBased annotation and associated warnings will help developers to migrate their Java code that uses identified JDK value-based classes in a way not compatible with inline classes to safer alternatives. JEP 390 not only spells out the details of this, but also provides evidence of the growing maturity of the Project Valhalla work on inline classes.

Friday, September 25, 2020

foojay - A Place for Friends of OpenJDK

I welcome new sources of information on all things Java and was excited to see what the foojay site has to offer after Geertjan Wielenga (@GeertjanW) pointed it out to me. In this post I highlight some of the most promising characteristics of this site for this seeking new information on Java-related topics.

The foojay.io About page describes the site's name: "A place for friends of OpenJDK." This relatively new Azul-sponsored community site was highlighted in Simon Ritter's 25 April 2020 post "foojay: A Place for Friends of OpenJDK." In that post, Ritter states:

For Java users and developers who depend on OpenJDK builds, finding the relevant highlights of a given update and how they may improve or otherwise impact deployments is no small task. Separating issues that have significance to a user from others would involve going through hundreds of individual issues. Following each into the Java Bug System to review details, and deducing relevance for one’s environment can be quite a challenge. In practice, few Java users or organizations will go through this level of effort.

This is where foojay's user-focused Java and OpenJDK update descriptions come in.

New Bug Fixes and Features in the OpenJDK

As the Ritter quote above demonstrates, one of the primary objectives of the foojay site is to help Java developers understand the various JDK offerings available based on OpenJDK and the changes, fixes, and new features associated with different versions of OpenJDK.

The following describes how to quickly see new features associated with various versions of OpenJDK on the foojay site:

  • The "What's New in OpenJDK?" is front and center on the Foojay page.
    • Provides overview of new features coming to recent versions of OpenJDK.
    • Click on month/year to see versions of OpenJDK with changes in that month.
    • Click on specific version of OpenJDK for that month/year to see change categories such as "Highlights", "All Issues", "Component View", and "Security / CVE View"
    • Click on category tab to see table representation of changes to that OpenJDK version in that category. This table includes different details depending on the category. The "Foojar Commentary" column of the "Highlights" table provides summary details about a particular bug or feature.

Java Version Almanac

The Java Version Almanac section of the Foojay site presents details on various versions from 1.0 to through JDK 16 as of this writing. There are, of course, generally more details in the later versions than the earlier versions, but it's interesting to see how Java and the JDK have changed. The information presented in Foojay's "Java Version Almanac" section is based upon javaalmanac.io, which "is provided by Java Champions Marc R. Hoffmann and Cay S. Horstmann".

OpenJDK Command Line Arguments

Another interesting section of the Foojay site is the section on OpenJDK's supported command-line arguments. This section shows command-line arguments for each version of OpenJDK from Java 6 through Java 16 as of this writing. This section is derived from Chris Newland's chriswhocodes.com site.

Foojay Blog

Since its inception, the foojay site seems to have broaded its scope to cover additional topics related to use of Java and OpenJDK. There are several well-known Java developers/speakers who write posts for the Foojay blog. These include the following authors (not the entire list):

Although many of the bloggers on the Foojay site have their own blog sites with the same blog posts, there is an advantage to being able to see the headlines and blogs of these different authors in one place without needing to go to each author's individual blog.

Things I Like About the Foojay Site

  • Coverage of different versions of Java and OpenJDK
  • Coverage of different implementations of OpenJDK
  • Community-based with contributions from multiple individuals and sites
  • Blog posts focused on Java-related technologies
    • Even though many of these posts are available on the authors' sites, it's easier to see the headlines in a single location and click on stories of interest.
    • I like the strong focus on Java in these blog posts because Java is still my main programming language.
  • Single location for updates on what's new in the Java and OpenJDK world
    • This Azul-sponsored site nicely complements the Oracle-provided Inside Java site and a Java developer is likely to be aware of most major developments in the Javasphere if regularly reviewing these two sites.
  • Significantly fewer advertisements than many software development aggregation sites
    • Although there are mentions of Azul offerings, they are limited and not the invasive pop-up style advertisements plaguing many software development aggregation sites.
  • Backed by Azul
    • Too many blogs and other sources that I like for new information on Java tend to die out quickly and I have greater confidence in the Azul-sponsored Foojay site being around for a while.
    • The Foojay site's association with Azul means that it is associated with an OpenJDK contributor who provides their own implementation based on OpenJDK and who is a participant in the Java Community Process.

Conclusion

We continue to have exciting new things coming soon to a JDK near us. With the 6-month cadence of new OpenJDK releases, it can be difficult to keep up with so much change. The Foojay site is a great tool for Java developers who want to see the most important details regarding what's coming without needing to delve into bug reports and OpenJDK mailing lists.

Friday, August 21, 2020

JDK16 javac xlint Warning about Default Constructors

I mentioned in my blog post "Explicit No-Arguments Constructor Versus Default Constructor" that "it is possible that one day javac will have an available lint warning to point out classes with default constructors." In that post, I referenced JDK-8071961 ("Add javac lint warning when a default constructor is created"), which has now been implemented as of JDK 16 Early Access Build #12. This post introduces that newly available javac -xlint warning.

To see this new javac -Xlint warning in action, one must download at least JDK 16 Early Access Build #12 (19 August 2020) or later.

To demonstrate the new javac -Xlint warning, we need a class with no explicit constructor so that javac will generate a "default constructor."

<rant>(By the way, a minor pet peeve of mine is when someone comments an explicit constructor with no arguments with Javadoc text that states "Default constructor." It's not really a default constructor once it's explicitly specified!)</rant>

An example of a class with no explicit constructor is available on GitHub and is shown here:

DefaultConstructor.java
package dustin.examples.jdk16;

import static java.lang.System.out;

/**
 * This class intentionally does NOT specify an explicit constructor
 * so that a "default constructor" will be generated and trigger the
 * new JDK 16 warning.
 */
public class DefaultConstructor
{
   private String name;

   public String getName()
   {
      return name;
   }

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

   public static void main(final String[] arguments)
   {
      final DefaultConstructor instance = new DefaultConstructor();
      instance.setName(arguments.length > 0 ? arguments[0] : "");
      out.println("Hello " + instance.getName() + "!");
   }
}

If we compile the new class with no explicitly specified constructor with javac provided by the OpenJDK JDK 16 Early Access Build #12 or later, we won't see the new warning demonstrated unless we export the package that class is in and enable -Xlint warnings. An example of exporting the package is available on GitHub and is shown here:

module-info.java
module dustin.examples
{
   exports dustin.examples.jdk16;
}

Compiling with -Xlint

When I run javac -X with the JDK 16 Early Access Build #12 compiler, I see these Xlint-related options that are now available (emphasis added):

  -Xlint                       Enable recommended warnings
  -Xlint:(,)*
        Warnings to enable or disable, separated by comma.
        Precede a key by - to disable the specified warning.
        Supported keys are:
          all                  Enable all warnings
          auxiliaryclass       Warn about an auxiliary class that is hidden in a source file, and is used from other files.
          cast                 Warn about use of unnecessary casts.
          classfile            Warn about issues related to classfile contents.
          deprecation          Warn about use of deprecated items.
          dep-ann              Warn about items marked as deprecated in JavaDoc but not using the @Deprecated annotation.
          divzero              Warn about division by constant integer 0.
          empty                Warn about empty statement after if.
          exports              Warn about issues regarding module exports.
          fallthrough          Warn about falling through from one case of a switch statement to the next.
          finally              Warn about finally clauses that do not terminate normally.
          missing-explicit-ctor Warn about missing explicit constructors in public classes in exported packages.
          module               Warn about module system related issues.
          opens                Warn about issues regarding module opens.
          options              Warn about issues relating to use of command line options.
          overloads            Warn about issues regarding method overloads.
          overrides            Warn about issues regarding method overrides.
          path                 Warn about invalid path elements on the command line.
          processing           Warn about issues regarding annotation processing.
          rawtypes             Warn about use of raw types.
          removal              Warn about use of API that has been marked for removal.
          requires-automatic   Warn about use of automatic modules in the requires clauses.
          requires-transitive-automatic Warn about automatic modules in requires transitive.
          serial               Warn about Serializable classes that do not provide a serial version ID. 
                             Also warn about access to non-public members from a serializable element.
          static               Warn about accessing a static member using an instance.
          text-blocks          Warn about inconsistent white space characters in text block indentation.
          try                  Warn about issues relating to use of try blocks (i.e. try-with-resources).
          unchecked            Warn about unchecked operations.
          varargs              Warn about potentially unsafe vararg methods
          preview              Warn about use of preview language features
          none                 Disable all warnings

As shown in these usage details, one can use -Xlint, -Xlint:all, or -Xlint:missing-explicit-ctor to see this new warning about default constructors being exposed by classes in publicly exported packages.

Compiling the new class with -Xlint, -Xlint:all, or -Xlint:missing-explicit-ctor demonstrates the new warning about default constructors being used in a formal API:

-Xlint:all

-Xlint and -Xlint:missing-explicit-ctor

As shown in the screenshots, the warning message states (emphasis added by me): "warning: [missing-explicit-ctor] class DefaultConstructor in exported package dustin.examples.jdk16 declares no explicit constructors, thereby exposing a default constructor to clients of module dustin.examples"

The warning message that javac provides when -Xlint is appropritely specified describes the issue and specifically calls out the exported package with the offending class and the name of module that exports that package.

Summary of Steps to See Default Constructor Warning

  1. Download and "install" OpenJDK 16 Early Access Build #12 (or later) from https://jdk.java.net/16/
  2. Write Java class with no explicitly specified constructor so that javac will generate a "default constructor" (example).
  3. Export package with class with no explicit constructor via a module-info.java file (example).
  4. Compile class with no explicit constructor with -Xlint:all provided to the javac compiler.

Not All Classes with No Explicit Constructors Will Be Flagged

Not all Java classes that lack an explicit constructor will lead to this new warning being emitted even when a relevant -Xlint option is specified. As stated earlier, even the DefaultConstructor class used in this post's example does not lead to the warning message being generated until it's package is exported in the module-info.java file. Joe Darcy explains on the OpenJDK compiler-dev mailing list:

In terms of detailed criteria to issue the new warnings, there was the usual tension in warnings between reducing false positives and false negatives. For example, warning for *any* default constructor, even in a throw-away class, would be more annoying than helpful. With some guidance from the JDK code base, criteria in the current patch are a default constructor merits a warning if:

  • The class is in a named package and the packaged has an unqualified export from its module AND
  • The class is public and, if it is a nested class, all of its lexically enclosing types are public too.

An unqualified export, where the package is available to use to any module and not just named ones, was taken to indicate classes in the package can comprise a "formal API". It would be simple to change this to an unqualified export, but I wanted to avoid unwanted instances of a new warning. If a public nested class is a non-public enclosing class, the nested class is not directly part of the exported API. These combinations of kinds of exports and nesting are tested in the tests in the DefaultCtor directory.

Why Warn on Use of Default Constructors in a "Formal API" Class?

The previously mentioned Joe Darcy post explains why this warning has been added:

Some background on the design of the warning and broader usage context, while default constructors can be convenient for informal code, they can be a bit troublesome for more formal APIs, such as the public classes of packages in the JDK. One issue is that default constructors do not have javadoc. Another is that a class that semantically should not be instantiated, say it is a solely a holder for static constants and methods, can get instantiated and subclassed. (Subclasssing such a class is an anti-pattern to allow use short names for the static members, which is no longer necessary since static imports as of Java SE 5.0.)

Conclusion

This seemingly small change to add this new warning about "default constructors" in "formal API" classes has required more effort than might initially be assumed. A large number of issues were written to not only introduce the xlint warning, but to clean up numerous classes throughout the JDK that triggered this warning when compiled. Furthermore, naming and logging can often be tricky and the particular warning mssage went through review and iterative changes as well.

Friday, August 7, 2020

Project Amber Design Documents on GitHub

As discussed in a Brian Goetz post on the amber-spec-experts mailing list, an effort is currently underway for "migrating design docs to GitHub." The GitHub repository is openjdk/amber-docs and it already hosts some frequently-referenced Project Amber design documents.

The project's main README states that this repository "is for design notes, presentations, guides, FAQs, and other collateral surrounding OpenJDK Project Amber." It also explains the relationship between the Amber design documents in this repository with those hosted on the OpenJDK Project Amber site: "Most documents here are written in (pandoc) Markdown, and changes made to Markdown sources are automatically formatted and pushed to the OpenJDK website.

The Amber design documents hosted on openjdk/amber-docs are currently categorized as either design-notes or guides. Here are some of the Amber design documents already hosted in the new openjdk/amber-docs GitHub repository (mostly in GitHub-friendly MarkDown format):

The moving of OpenJDK source and artifacts to GitHub via Project Skara is paying benefits already in terms of accessibility for Java developers. The moving of Amber design documents to GitHub will likewise make these documents more readily available. As a side note, the OpenJDK design discussions and decisions are also often readily available from the relatively new Inside.java blog that features "news and views from the Java team at Oracle."

Although these Project Amber design documents have long been publicly available, this migration to GitHub should make them even easier to access and will likely increase awareness of and access to these documents. Undoubtedly, advantages inherent with a modern version control system will be gained including easier ability to make, review, track, audit, and contribute changes.

Saturday, August 1, 2020

Finalizing instanceof Pattern Matching and Records in JDK 16

Gavin Bierman has recently posted two approachable posts regarding instanceof pattern matching and records on the amber-spec-experts mailing list and both posts are thorough but succinct. I recommend that anyone interested in the latest state of these two Project Amber initiatives read these posts: "Finalizing in JDK 16 - Pattern matching for instanceof" and "Finalizing in JDK 16 - Records". Even for those not interested in the details, it is probably interesting to see the likelihood of instanceof pattern matching and records being finalized in JDK 16.

Pattern Matching instanceof Operator

In "Finalizing in JDK 16 - Pattern matching for instanceof," Bierman writes: "In JDK 16 we are planning to finalize two JEPs: Pattern matching for instanceof and Records." About instanceof pattern matching, Bierman states, "Adding conditional pattern matching to an expression form is the main technical novelty of our design of this feature."

The "Finalizing in JDK 16 - Pattern matching for instanceof" post talks about advantages of the instanceof pattern matching approach. Advantages discussed in the post include refactoring of a "common programming pattern" (checking for instanceof and then explicitly casting the thing checked with instanceof to the type it was checked against) to smaller code that is more readable. Another discussed advantage is the ability "to compactly express things with expressions that are unnecessarily complicated using statements."

Bierman also discusses scope issues, local pattern variable "poisoning", and how these will be dealt with using "flow scoping." About this, Bierman writes:

Java already uses flow analysis - both in checking the access of local variables and blank final fields, and detecting unreachable statements. We lean on this concept to introduce the new notion of flow scoping. A pattern variable is only in scope where the compiler can deduce that the pattern has matched and the variable will be bound. This analysis is flow sensitive and works in a similar way to the existing analyses. ... The motto is "a pattern variable is in scope where it has definitely matched". This is intuitive, allows for the safe reuse of pattern variables, and Java developers are already used to flow sensitive analyses.

The post on instanceof pattern matching ends with a discussion of the "Swiss cheese property" ("unfortunate interaction of flow scoping and shadowing [local pattern variables shadowing a field declaration]") and expresses the hope that IDEs will help developers to deal with these unfortunate interactions.

Records

Bierman's post "Finalizing in JDK 16 - Records" provides some nice summary descriptions related to Records. The first is, "Record classes are a special kind of class that are used primarily to define a simple aggregate of values." This is immediately followed by, "Records can be thought of as nominal tuples; their declaration commits to a description of their state and given that their representation, as well as all of the interesting protocols an object might expose -- construction, property access, equality, etc -- are derived from that state description."

This post covers the "implicit declaration of a number of members" of a Record from the Record's "state description." The post also provides a brief overview of why a developer might "explicitly [provide] a canonical constructor" and how to do so. About this, Bierman writes, "The intention of a compact constructor declaration is that only validation and/or normalization code need be given in the constructor body; the remaining initialization code is automatically supplied by the compiler."

"Finalizing in JDK 16 - Records" also discusses canonical constructor accessibility, @Override, and general annotations related to Records. The post concludes by mentioning that Records can be declared locally and explains how this is an advantage for an expected common use case of Records: "Records will often be used as containers for intermediate data within method bodies. Being able to declare these record classes locally is essential to stop proliferation of classes."

Conclusion

The two Project Amber initiatives of instanceof pattern matching and records will be welcome additions to the Java programming language. Although these features have been available in versions of the JDK before JDK 16 as preview features, it will be their finalization (hopefully in JDK 16) that will enable many more developers to apply them in production code.

Friday, July 10, 2020

Potential Inlined JDK Classes

Earlier this week, Dan Smith posted "JEP draft: Identity Warnings for Inline Class Candidates" on the valhalla-spec-experts mailing list. The purpose of this post was to introduce a draft JEP ("Identity Warnings for Inline Class Candidates") associated with JDK-8249100.

The main intent of this JEP is to "provide warnings at compile time and runtime for behaviors that will change when certain classes of the Java SE APIs become inline classes." Although I think it's a good idea to have these warnings to help Java developers recognize coming change and opportunities for change, I find the background details provided to in this draft JDK Enhancement Proposal to be as interesting as the end objective. This draft JEP consolidates discussions and decisions discussed among many different threads and messages on the mailing lists in a single easily referenced location.

Inline Classes in Java

The "Motivation" section of the draft JEP states that inline classes "declare their instances to be identity-free and capable of 'inline' or 'flattened' representations, where instances can be copied freely between memory locations and encoded using solely the values of the instances' fields." That same section also outlines two operations (monitoring/locking with synchronized and instantiation with new) that will not be supported by the identity-less inline classes. Because certain operations will not be allowed on inline classes and because some current JDK classes that may be used with these operations may be transitioned to inline classes at some point, it is desirable to have this draft JEP's proposed warnings. These warnings alert developers to their current uses of these inline class candidate JDK classes in conjunction with these operations that will not be supported on inline classes in the future (eventually an error will result).

For more background on inline classes, see the March 2020 version of the State of Valhalla document, in which Brian Goetz makes these observations:

  • "Inline classes are heterogeneous aggregates that explicitly disavow object identity."
  • Inline classes "must be immutable and cannot be layout-polymorphic."
  • "Inline classes have some restrictions compared to ordinary (identity) classes; they are final, their fields are final, and their ability to participate in inheritance is limited."
  • "Inline classes can use most mechanisms available to classes: methods, constructors, fields, encapsulation, type variables, annotations, etc"
  • "The slogan" for inline classes "is Codes like a class, works like an int."
  • "Inline types could equally be thought of as 'faster classes' or 'user-definable primitives'."

Identified JDK Inline Class Candidates

The "Description" section of the draft JEP lists several bullets of classes and groups of classes that are "likely to become inline classes." These candidate JDK inline classes are listed, as of this writing, as follows:

  • Primitive Wrapper Classes: java.lang.Byte, java.lang.Short, java.lang.Integer, java.lang.Long, java.lang.Float, java.lang.Double, java.lang.Boolean, java.lang.Character
  • Optional Classes: java.util.Optional, java.util.OptionalInt, java.util.OptionalLong, java.util.OptionalDouble
  • Java Time Classes: java.time.Instant, java.time.LocalDate, java.time.LocalTime, java.time.LocalDateTime, java.time.ZonedDateTime, java.time.ZoneId, java.time.OffsetTime, java.time.OffsetDateTime, java.time.ZoneOffset, Duration, java.time.Period, java.time.Year, java.time.YearMonth, java.time.MonthDay, and select classes in java.time.chrono package.
  • Collections Returned from Collection Factories: java.util.List.of, java.util.List.copyOf, java.util.Set.of, java.util.Set.copyOf, java.util.Map.of, java.util.Map.copyOf, java.util.Map.ofEntries, and java.util.Map.entry
  • java.lang.Runtime.Version
  • java.lang.ProcessHandles Implementations
  • java.lang.constant.ConstantDesc Implementations (except java.lang.String)
  • Select Classes in the jdk.incubator.foreign Module

JDK Classes That Will Not Be Inlined

There are some JDK-provided classes that, at first thought, might seem like candidates to be inline classes, but turn out to have characteristics that make them unlikely to ever be transitioned. These include java.lang.String and java.math.BigInteger and java.math.BigDecimal.

Conclusion

The recently proposed draft JEP discussed in this post provides a centralized location for updated background details related to the currently planned inline classes feature being discussed and worked as part of Project Valhalla. Keeping in mind that this is a draft JEP about an in-work feature, this draft JEP provides insight into how inline classes will change how we write and use Java classes.