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

Friday, January 31, 2020

Source Code for Effective Java Third Edition Updated to Use Newer Features

Those who have read the Third Edition of Effective Java are likely aware of the source code associated with that book available on GitHub. The jbloch/effective-java-3e-source-code project has 1700+ stars and has been forked nearly 800 times as of this writing. The version of Java featured in the Third Edition of Effective Java is largely JDK 8 with some coverage of JDK 9 (see my earlier post for details on what is covered in this third edition).

Much has been added to the JDK since the publication of the Third Edition of Effective Java and many new releases have arrived with the faster 6-month cadence. Given this, I was particularly interested to see in an amber-spec-experts mailing list post that Rémi Forax has forked jbloch/effective-java-3e-source-code into the GitHub project forax/effective-java-3e-source-code that has "taken the source of Effective Java (3rd Ed) and change them to use var, switch expression, records and the instanceof with the type test pattern."

There are several things that I like about the idea of refreshing examples from Effective Java (Third Edition) to use newer features:

  • Developers can see how to apply effective Java practices using recently released features.
  • Developers can view the differences between the JDK 8/9 versions and the newer versions to see how new constructs replace older constructs and thus gain a better understanding of the newer constructs.
  • It is useful to see some of the changes when deciding whether a particular change to use a newer construct really helps with code readability in a given situation.

The main page for the forked forax/effective-java-3e-source-code (README.md) states, "The source code have been updated to use new constructs available since Java 9, the version used by the 3rd edition." That page then provides bullets on the types of new constructs applied to the source code with links to each new construct's associated JDK Enhancement Proposal (JEP).

As of this writing, Commit 275eef87e4661f7f1edc41f4730cecf7a1096a97 is the main commit of interest. It covers changes to 113 files. I'll call out a few specific changes here to illustrate the types of changes applied (some of which are to apply preferred constructs that were available even before JDK 9):

Conclusion

The ability to view changes to the original source code associated with the Third Edition of Effective Java to accommodate new language constructs is highly useful in terms of learning about the new constructs and how they relate to or replace old constructs and in deciding if the differences are desirable in different situations.

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.

Monday, April 9, 2018

Clearer Code with JDK 10 Local Variable Type Inference

One of the first fruits of Project Amber, Local-Variable Type Inference (JEP 286), has been delivered with JDK 10. JEP 286's "Summary" describes its purpose, "Enhance the Java Language to extend type inference to declarations of local variables with initializers." In conjunction with this release, Stuart Marks has published the March 2018 article "Style Guidelines for Local Variable Type Inference in Java."

In "Style Guidelines for Local Variable Type Inference in Java," Marks postulates four "Principles" that lead to seven "Guidelines" that help developers to apply var properly to "help improve good code, making it shorter and clearer without compromising understandability." The articulated guidelines attempt to strike a balance that brings benefits of less redundant code with the benefits of explicitly readable code. The article outlines cases where var should be used and where it shouldn't be used. In general var is best used when other naming conventions or other constructs in use provide significant detail about the local variable type that is only repeated with the explicit typing. On the other hand, there are cases where much information is lost if the explicit type is removed and in such cases, use of var is discouraged. Another typical case where use of local variable type inference might be preferred is when the explicit typing is difficult to read and is only used in an intermediate step. It may not be important to see the complex explicit type for that intermediate step.

I couldn't help but think about my earliest days working with Groovy while reading the document "Style Guidelines for Local Variable Type Inference in Java." Most of the literature I read sang the virtues of using Groovy's def keyword wherever possible to conserve keystrokes and make the Groovy code more concise. In an effort to appear to "speak Groovy" fluently, I embraced what I deemed to be "idiomatic Groovy." However, after several months of this, I realized that some uses of the def keyword significantly reduced the readability of my Groovy code when I returned to it months after writing it. I began to use personal guidelines similar to those espoused by Marks with Java's LVTI with my Groovy code.

I wasn't the only one who learned that mindless application of Groovy's handy def keyword could actually lead to less maintainable Groovy code. Benefits that can be gained from explicit typing in Groovy are discussed in the StackOverflow thread "Groovy: 'def' keyword vs concrete type." The book Programming Grails has a section called "'def' Considered Harmful" in its first chapter and Rob Hinds's post "Groovy: A Retrospective" states, "... typing is better - I still don't buy that typing def rather than the actual type is actually that much of a saving." The "official" Groovy Style Guide features a section "Optional typing advice" that talks about whether or not to use def. However, for the similar case to which Java LVTI applies (local variables), the advice is, "you're more free to decide when to type or not."

Like so many other Java APIs and language constructs, the best Java code will not be written to always use LVTI or to never use LVTI. Instead, as is so often the case in software development, the decision on whether to use LVTI or not to use LVTI depends on the context in which it would potentially be used. Reviewing the "Style Guidelines for Local Variable Type Inference in Java" document and trying out LVTI and reviewing others' code using LVTI are the best ways to build an intuitive sense for when to apply LVTI and when not to. The amber-dev mailing list thread starting with the message "LVTI Style Guide" provides significant more discussion surrounding this document on when to use and not use Java's Local Variable Type Inference. The blog post "Representing the Impractical and Impossible with JDK 10 'var'" demonstrates how to "declare variables with types that were erstwhile impractical or impossible to represent" using Local Variable Type Inference.

Wednesday, February 21, 2018

JDK 10: FutureTask Gets a toString()

I've felt for a long time that, for most Java classes that have distinguishing attributes, developers should take the time to override Object.toString(), even if it's just with an IDE-generated implementation or using a library class such as Apache Commons Lang's ToStringBuilder. The overloaded Objects.toString() methods also make this easier than ever if one wants to implement toString by hand. The JDK class FutureTask, introduced with J2SE 5, finally gets its own toString() implementation in JDK 10.

Richard Nichols's 2012 post "How to get the running tasks for a Java Executor..." highlights the omission of a toString() method on the FutureTask class. He wrote:

It seems odd that the API doesn't include any way to gather info about what's happening inside the Executor, and also, there's not even a toString() implementation for wrapping classes like FutureTask which would bubble your Runnable or Callable classes' toString() methods.

Nichols's post is in the context of his observation that "it's quite difficult to actually expose at run-time what ... Java's Executor is actually doing at any point in time."

Issue JDK-8186326 ["Make toString() methods of "task" objects more useful"] talks about aligning FutureTask toString() with that of CompletableFuture, which the issue states "already has a useful toString method, giving the current status." An e-mail thread in late 2017 documents the discussions around the addition of toString() to FutureTask and other "task classes in j.u.c." (java.util.concurrent).

The Javadoc comments for the new FutureTask.toString() method state, "The default implementation returns a string identifying this FutureTask, as well as its completion state. The state, in brackets, contains one of the strings 'Completed Normally', 'Completed Exceptionally', 'Cancelled', or 'Not completed'." Three of these four potential completion states for FutureTask's toString() are also potentially written as part of CompletableFuture's toString() ["Cancelled" is the exception].

The addition of a specific implementation of toString() to the FutureTask class in JDK 10 is a small one. However, for a developer "staring at output of toString for 'task' objects (Runnables, Callables, Futures) when diagnosing app failures" as described in JDK-8186326's "Problem" statement, this "small" addition is likely to be very welcome.

Tuesday, February 20, 2018

JDK 10: Accessing Java Application's Process ID from Java

A popular question on StackOverflow.com is, "How can a Java program get its own process ID?" There are several answers associated with that question that include parsing the String returned by ManagementFactory.getRuntimeMXBean().getName() [but that can provide an "arbitrary string"], using ProcessHandle.getPid() [JEP 102], using Java Native Access (JNA), using System Information Gatherer And Reporter (SIGAR), using JavaSysMon, using Java Native Runtime - POSIX, parsing the results of jps (or jcmd) via invocation of Runtime.getRuntime().exec(String), and other approaches. JDK 10 introduces perhaps the easiest approach of all for obtaining a JVM process's PID via a new method on the RuntimeMXBean.

JDK-8189091 ("MBean access to the PID") introduces the RuntimeMXBean method getPid() as a default interface method with JDK 10. That issue states the "Problem" as: "The platform MBean does not provide any API to get the process ID of a running JVM. Some JMX tools rely on the hotspot implementation of RuntimeMXBean::getName which returns < pid >@< hostname >." The issue also provides the "Solution": "Introduced new API java.lang.management.RuntimeMXBean.getPid, so that JMX tools can directly get process ID instead of relying on the implementation detail, RuntimeMXBean#getName().split("@")[0]."

The next code listing is a simple one and it demonstrates use of this new getPid() method on RuntimeMXBean.

Using JDK 10's RuntimeMXBean.getPid()

final RuntimeMXBean runtime = ManagementFactory.getRuntimeMXBean();
final long pid = runtime.getPid();
final Console console = System.console();
out.println("Process ID is '" + pid + "' Press <ENTER> to continue.");
console.readLine();

When the code above is contained within an executable main(String[]) function and that function is executed from the command line, the output is as shown in the next screen snapshot (which also includes a separate terminal used to verify the PID is correct via jcmd).

The process ID is provided as a long and no parsing of an "arbitrary string" is necessary. This approach also does not require a third-party library or elaborate code to determine the current Java process's identifier.

This post has provided a brief introduction to what will perhaps be the easiest approach for a Java application (written with JDK 10 or later) to determine its own underlying process ID.

Monday, February 19, 2018

JDK 10's Summary Javadoc Tag

JDK 10 introduces a Javadoc tag {@summary} via issue JDK-8173425 ("Javadoc needs a new tag to specify the summary."). This new tag allows the developer to explicitly specify what portion of the Javadoc comment appears in the "summary" rather than relying on Javadoc's default treatment looking for a period and space to demarcate the end of the summary portion of the comment. JDK-8173425 states, "Currently in javadoc the summary (firstsentence) of an element is deciphered by a dot-space rule, or if required using BreakIterator." It adds that it can be confusing to know what that implicitly selected summary sentence will be.

The easiest way to see {@summary} in action may be through Javadoc examples. The next code listing shows four methods with similar Javadoc comments, two using explicit {@summary} tags and two relying on implicit Javadoc summary construction.

Demonstrating {@summary} in Javadoc Method Comments

package dustin.examples.javadoc;

/**
 * Demonstrate JDK 10 added summary support. Demonstrates
 * this by comparing similar methods' Javadoc comments with
 * and without use of new "@summary" tag.
 */
public class Summary
{
   /**
    * This method's first sentence is normally in the summary.
    * Here are some of its characteristics:
    * <ul>
    * <li>This method does great things.</li>
    * <li>This method does not really do anything.</li>
    * </ul>
    */
   public void implicitSummary1()
   {
   }

   /**
    * This method's first sentence is normally in the summary.Here are some of its characteristics:
    * <ul>
    * <li>This method does great things.</li>
    * <li>This method does not really do anything.</li>
    * </ul>
    */
   public void implicitSummary2()
   {
   }

   /**
    * {@summary This method's first sentence is normally in the summary.
    * Here are some of its characteristics:
    * <ul>
    * <li>This method does great things.</li>
    * <li>This method does not really do anything.</li>
    * </ul>}
    */
   public void explicitSummary1()
   {
   }

   /**
    * {@summary This method's first sentence is normally in the summary.Here are some of its characteristics:
    * <ul>
    * <li>This method does great things.</li>
    * <li>This method does not really do anything.</li>
    * </ul>}
    */
   public void explicitSummary2()
   {
   }
}

When the Javadoc tool delivered with the first JDK 10 (18.3) Release Candidate (Build 43) is executed against this simple class, the "Method Summary" section of the generated HTML appears as follows in a web browser.

Comparing the HTML output to the commented Java code above it demonstrates how the {@summary} allows for explicit control of what appears in the methods' summaries.

Friday, February 16, 2018

First JDK 10 (18.3) Release Candidate (Build 43) Demonstrates New Versioning Scheme

Mark Reinhold's post "JDK 10: First Release Candidate" announced "no unresolved P1 bugs in build 43" and named that Build 43 the initial JDK 10 Release Candidate. The Reinhold post also points to the "JDK 10 Early-Access Builds" page which contains links to the release notes; to the Javadoc-based API documentation; to the "early-access, open-source builds" (OpenJDK) for Windows, Linux, macOS, and Alpine Linux; and to the Oracle JDK builds.

The following screen snapshot shows the version information provided by the OpenJDK 10 Build 43 (the text in the screen snapshot is reproduced below the image):

openjdk version "10" 2018-03-20
OpenJDK Runtime Environment 18.3 (build 10+43)
OpenJDK 64-Bit Server VM 18.3 (build 10+43, mixed mode)

The next screen snapshot shown the version information provided by the Oracle JDK 10 Build 43 (the text in the screen snapshot is reproduced below the image):

java version "10" 2018-03-20
Java(TM) SE Runtime Environment 18.3 (build 10+43)
Java HotSpot(TM) 64-Bit Server VM 18.3 (build 10+43, mixed mode)

As the above screen snapshots show, the -version information for the OpenJDK and OracleJDK currently show both forms. They show the "10" in quotes for JDK 10, but they also show 18.3. This is consistent with the JSR 383 title ["Java SE 10 (18.3) Platform JSR (383)"] and its description.

There has been some confusion regarding the versioning scheme for versions of Java after JDK 9 because of rapid changing developments in plans for Java version names. Some key posts on the developing version naming after JDK 9 are shown below.

  1. Moving Java Forward Faster (6 September 2017)
    • Proposed that "after Java 9 we 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."
    • "To make it clear that these are time-based releases, and to make it easy to figure out the release date of any particular release, the version strings of feature releases will be of the form $YEAR.$MONTH." This is where the "18.3" comes from in the above examples (representing March 2018).
    • Related post "Accelerating the JDK release cadence" discusses approaches to be taken with "the ultimate goal" of making "OpenJDK and Oracle JDK builds completely interchangeable."
  2. Version-string schemes for the Java SE Platform and the JDK (19 October 2017)
    • Addresses community concern and responses (such as this one) to original proposal.
    • Outlines criteria to be considered when selecting a versioning scheme.
    • Presents potential alternatives that satisfy the outlined criteria.
    • References Wadler's Law.
  3. Proposal: Newer version-string scheme for the Java SE Platform and the JDK (2 November 2017)
    • Introduces scheme $FEATURE.$INTERIM.$UPDATE.$EMERG
    • $FEATURE is "the feature-release counter, incremented every six months regardless of release content."
    • "This is primarily a time-based scheme, since $FEATURE is incremented every six months regardless of release content and, for each feature release, $UPDATE is incremented every three months."
    • JEP 223-compliant system property java.version.date is added and is the "intended GA date" in "ISO-8601 YYYY-MM-DD format." It is "some date in the future" for early access releases. In the examples above, the expected release General Availability release date is 2018-03-20.
  4. Updating the version number (1 December 2017)
    • States that JSR-383 documents will be updated to reference "10 (18.3)" instead of "18.3".
  5. Why do "Oracle JDK 10 builds" not support AppCDS? (16 February 2018)
    • I include this post because it provides a specific concrete example of how the version name differs for early access builds ("10-ea+42") versus release candidate builds intended for eventual general availability ("10+43").

The screen snapshots shown in this post depict the versions associated with the available initial build of JDK 10 Release Candidate. This initial build provides an early look at the new JDK version naming scheme in action.

Monday, February 12, 2018

APIs To Be Removed from Java 10

In the blog post "JDK 10 Release Candidate Phase", I looked at the twelve new features that are likely to be part of JDK 10. In this post, I look at some of the APIs that appear likely to be removed in JDK 10 and I look at some APIs proposed to be deprecated in JDK 10. The information in this post is based on the current version (2018/1/31 19:49 -0800 [a337d4f5aa79]) of "Java SE 10 (18.3) (JSR 383) Proposed Final Draft Specification - DRAFT" and, because this source document is a draft, this information is subject to change.

The JDK 9 enhanced deprecation feature (JEP 277) allowed several JDK APIs to be annotated with @Deprecated's new optional forRemoval() element to be set to true, which indicates that the annotated API "is earmarked for removal in a future release." The following APIs had this enhanced deprecation applied, were marked for "removal in a future release" in JDK 9, and now JDK 10 appears to be the version in which they'll be removed.

As currently proposed, JDK 10 will add the optional annotation element forRemoval=true to some previously deprecated [@Deprecated] API elements that did not formerly have forRemoval set. These include security-related interfaces, classes, and exceptions that have been replaced by other security-related constructs (many were replaced as far back as JDK 1.2). Several "finalize"-ending methods also have their @Deprecated annotation enhanced to indicate that as of JDK 10 they are "earmarked for removal in a future release." For example, FileInputStream.finalize() was deprecated in JDK 9, but will be marked with forRemoval=true as of JDK 10.

Most of the JDK API constructs to be removed in JDK 10 have been deprecated for a long time (since JDK 1.2 or earlier) and so there has been plenty of time to replace use of these. Most of the JDK API constructs newly denoted in JDK 10 to be removed in a future release have already been deprecated and this change is only to indicate that they now will likely be removed in a future release. The JDK 9-introduced enhanced deprecation mechanism allows the JDK API to have constructs more aggressively removed after having their likely removal advertised in advance via the forRemoval=true element of the @Deprecated annotation. JDK 9 removed several things and it appears likely that JDK 10 will continue the removal of certain previously deprecated API elements.

Thursday, February 1, 2018

JDK 10 Release Candidate Phase

Mark Reinhold's mailing list message "JDK 10 Release Candidate Phase: Process proposal" was posted today and opens with the sentence, "Per the JDK 10 schedule, we'll enter the Release Candidate Phase next week, on Thursday, 8 February." Reinhold proposes that the JDK 10 Release Candidate Phase be enacted in the same manner as the JDK 9 Release Candidate Phase and he provides a link to the (proposed) "JDK 10 Release-Candidate Phase" page. That page outlines the proposed processes associated with the JDK 10 Release Candidate Phase with specific focus on the level of bug fixes targeted for JDK 10. The bugs are categorized as P1 through P5 and once the JDK 10 Release Candidate Phase is entered, "all P2-P5 bugs must be left to future releases" and the focus will be on fixing "all P1 bugs that are new in JDK 10 and critical to the success of the release."

At the time of this writing, the following twelve features are targeted for JDK 10 as we're about to enter the Release Candidate Phase:

Additional References

Saturday, January 20, 2018

Immutable Versus Unmodifiable in JDK 10

Nearly two months ago, Stuart Marks wrote, "Immutability is like wine." He then reminded readers of Schopenhauer's Law of Entropy: "If you put a spoonful of wine in a barrel full of sewage, you get sewage. If you put a spoonful of sewage in a barrel full of wine, you get sewage." With that provided background, Marks applied Schopenhauer's Law of Entropy to immutability with "immutability" replacing "wine" and "mutability" replacing "sewage" to make this insightful observation:

Similarly, if you add a little immutability to something mutable, you get mutability. And if you add a little mutability to something immutable, you get mutability.

The context of this quotation is an online discussion starting in October regarding JDK 10-targeted JDK-8177290 ("add copy factory methods for unmodifiable List, Set, Map") and JDK-8184690 ("add Collectors for collecting into unmodifiable List, Set, and Map"). JDK-8177290 is a subtask of JDK-8156070 ("Immutable Collections enhancements"), which is described as "a container for various enhancements and improvement subtasks for the immutable collections." The discussion is rather lengthy with multiple and often quite different perspectives involving terms such as "immutable" and "unmodifiable." Indeed, in the first post in this discussion, Mark writes, "The term 'immutable' is inextricably intertwined with 'persistent' when it comes to data structures, and I believe we'll be explaining this forever if Java's 'immutable' means something different from everybody else's."

Pointers to the final determination on terminology to use can be found in the current text associated with JDK-8191517 ("Add copy factory methods for unmodifiable List, Set, Map"). This text includes this statement, "Provide definitions for 'view' collections, 'unmodifiable' collections, and 'unmodifiable view' collections." JDK-8191517 also references webrev.4.zip and specdiff.4.zip for additional low-level details. The remainder of this post will look at some of the low-level details documented in those referenced ZIP files.

The Javadoc comments added to select interfaces' source code in the referenced zip files contain additional details regarding the terms "'view' collections," "'unmodifiable' collections," and "'unmodifiable view' collections." For example, the Javadoc for java.util.Collection has the following descriptions added to its interface-level Javadoc comment:

  • "View Collections" - "Most collections manage storage for elements they contain. By contrast, view collections themselves do not store elements, but instead they rely on a backing collection to store the actual elements. Operations that are not handled by the view collection itself are delegated to the backing collection."
  • "Unmodifiable Collections" - "An unmodifiable collection is a collection, all of whose mutator methods ... are specified to throw UnsupportedOperationException. Such a collection thus cannot be modified by calling any methods on it. For a collection to be properly unmodifiable, any view collections derived from it must also be unmodifiable."
    • Regarding Modifications: "An unmodifiable collection is not necessarily immutable. If the contained elements are mutable, the entire collection is clearly mutable, even though it might be unmodifiable. ... However, if an unmodifiable collection contains all immutable elements, it can be considered effectively immutable."
  • "Unmodifiable View Collections" - "An unmodifiable view collection is a collection that is unmodifiable and that is also a view onto a backing collection. Its mutator methods throw UnsupportedOperationException}, as described above, while reading and querying methods are delegated to the backing collection. The effect is to provide read-only access to the backing collection."
    • Regarding Modifications: "Note that changes to the backing collection might still be possible, and if they occur, they are visible through the unmodifiable view. Thus, an unmodifiable view collection is not necessarily immutable. However, if the backing collection of an unmodifiable view is effectively immutable, or if the only reference to the backing collection is through an unmodifiable view, the view can be considered effectively immutable."
    • Examples: "[Collections] returned by Collections.unmodifiableCollection [and] Collections.unmodifiableList."

The bullets above look in detail at the comments added to the Javadoc for the java.util.Collection class, but Javadoc comments for other collections interfaces also have significant new commentary regarding immutability and unmodifiability related to those specific interfaces. For example, the java.util.List interface Javadoc comment shown in the previously referenced ZIP files discusses "Unmodifiable Lists", convenient mechanisms available to access such Lists, and characteristics of Lists retrieved through those mechanisms. The Javadoc comments for the java.util.Set and java.util.Map interfaces receive similar treatment.

So far, I've mostly focused on how the Javadoc documentation is being enhanced and how the terminology is being changed from "immutable" to "unmodifiable." It is worth pointing out here, however, that this change in terminology is associated with the addition of new "copy factory methods" and new Collectors that will make it easier to access unmodifiable collections. JDK-8191517 summarizes these new methods:

  • "Add a family of copyOf() methods to java.util.List, Set, and Map to copy the elements from an existing collection or Map."
  • "Add a family of collectors to java.util.stream.Collectors that will create an unmodifiable List, Set, or Map from a stream."

The Javadoc comment for the forthcoming Map.copyOf(Map) method states, "Returns an unmodifiable Map containing the entries of the given Map. The given Map must not be null, and it must not contain any null keys or values. If the given Map is subsequently modified, the returned Map will not reflect such modifications." An interesting (but not surprising) "Implementation Note" in the Javadoc comment states, "If the given Map is an unmodifiable Map , calling copyOf will generally not create a copy." The numerous overloaded Map.of() methods added to Map with Java 9 have their Javadoc comments modified to replace "immutable" with "unmodifiable" and to replace references to the section titled "Immutable Map Static Factory Methods" with references to the new name for that section ("Unmodifiable Maps"). The term "structurally immutable" has also been replaced by "unmodifiable."

The Set.copyOf(Collection) and List.copyOf(Collection) methods coming to Java 10 are similar to that described in the last paragraph for Map.copyOf(Map) and include the same changes in comment terminology mentioned for Map.

The additions to the Collectors class in Java 10 described by JDK-8191517 are the four methods toUnmodifiableList(), toUnmodifiableSet(), and two overloaded versions of toUnmodifiableMap(-) (one version accepts a BinaryOperator parameter).

As the virtues of immutability are being more generally realized and as Java developers strive to apply immutability more often in their applications, it is typically important to know precisely how a given structure, collection, or view can be modified. JDK 10 is slated to add more methods to make it easier for Java developers to achieve immutability (or at least unmodifiability) of the collection and the comments on the most important interfaces and on the Collections class should help developers to more clearly understand what is mutable and what is not mutable in the constructs they select for their applications.

Saturday, December 23, 2017

Switch Expressions Coming to Java?

A JEP draft has been created with the title, "Switch Expressions for the Java Language." The current "Summary" states, "Extend the switch statement so that it can be used as either a statement or an expression, and improve how switch handles nulls. These will simplify everyday coding, as well as prepare the way for the use of pattern matching in switch." There are several promising characteristics of the proposed Java switch expression in its own right in addition to its enabling of the exciting Pattern Matching JEP (305).

The Java Tutorial defines a Java statement as a "complete unit of execution" that is "roughly equivalent to sentences in natural languages." It defines a Java expression as "a construct made up of variables, operators, and method invocations ... that evaluates to a single value." The current Java switch is a statement, but this draft JEP proposes that switch be supported as an expression as well.

The Switch Expression draft JEP states in its "Motivation" section, "As we prepare to enhance the Java Language to support pattern matching, several irregularities of the existing switch statement -- which have long been an irritation to users -- become impediments." The draft JEP is highly readable and contains interesting discussion points and illustrative code examples of how Java switch statement is currently often used within other Java constructors to effectively serve as an expression. The JEP draft shows how the proposal to extend switch to work as an expression in addition to being supported as a statement would improve the readability and maintainability of the code.

Java expressions need to "evaluate to a single value." The currently proposed switch expression would allow the break keyword to serve like a return in a Java method. A value could be specified following the break keyword and that value would be "returned" from the switch expression.

Because a Java expression "evaluates to a single value," a Java switch used as an expression would necessarily be required to provide a default value. An interesting twist on this covered in the draft JEP is for enums. The draft JEP states, "... in the case of an enum switch expression that covers all known cases (and eventually, switch expressions over sealed types), a default clause can be inserted by the compiler that indicates that the enum definition has changed between compile time and runtime. (This is what developers do by hand today, but having the compiler insert it is both less intrusive and likely to have a more descriptive error message than the ones written by hand.)" I found this particularly interesting because I've seen many cases where developers have either not written a "default" for a switch on the enum because all enum values at that time were covered by cases or have written a very generic error message. In fact, running into multiple examples of the latter are what finally led me to write my blog post "Log Unexpected Switch Options."

The draft JEP also covers some proposed enhancements that would benefit both the current switch statement and the new switch expression. One of these is the ability to indicate how to handle a null reference type passed to the switch statement. Today, for example, if one passes a null String to a switch statement, a NullPointerException is thrown. With this proposal, the developer could specify a case null clause. The current proposal would treat any null not explicitly handled as null is treated in switch today (by throwing a NullPointerException).

A second proposed beneficial feature for both current switch statement and proposed switch expression is to allow an individual case clause to support multiple potential matching values separated by commas. Three values for which the same behavior applies could be specified with a single case rather than with three case clauses sharing a single break.

The "switch expression draft" is still a draft that doesn't even have a number assigned to it yet and therefore is highly vulnerable to potential changes. Indeed, the proposal has already been discussed in the mailing lists and modified quite a bit, with an example being "Switch expressions -- some revisions." I found one message on the mailing list, "Switching on float/double/long," to be particularly interesting. This interesting message is full or technical and historical details including background on why today's switch statement does not support long and an interesting analysis provided by "resident floating-point expert" Joe Darcy regarding floating-point comparisons.

David Goldberg's 1991 article "What Every Computer Scientist Should Know About Floating-Point Arithmetic" is a well-known resource on understanding the complexities of floating-point arithmetic and representation. Darcy provides some interesting insight into some of these concerns and the IEEE 754 floating-point. He writes, "To address some common misunderstandings of floating-point, while it is often recommended to *not* compare floating-point values for equality, it is perfectly well-defined to do such comparisons, it just might not do what you want." Darcy addresses handling of NaN, positive zero and negative zero, and positive infinity and negative infinity. An interesting conclusion is that allowing switch on floating-point numeric type float could be implemented to really switch on the value provided by Float.floatToIntBits(float).

It appears that there could be some exciting times ahead for Java's switch if it is extended so that it can be used as an expression in addition to its current use as a statement. It's a development that could lead to cleaner code with less room for errors.

Wednesday, April 12, 2017

Use Cases for Java Enhanced Enums

In the message Enhanced Enums -- use cases, Brian Goetz writes, "We're hoping to get user feedback on the feature [Enhanced Enums] as it is now implemented." He states the first purpose of his message, "To get things started, here are some typical use cases where generic enums might be useful." The first of the two presented examples is refactoring com.sun.tools.javac.code.Dynamic class and its eight factory methods returning different instances of BootstrapArgument with different instances of its nested Kind enum into a single method using the dynamic enum.

The second use case example of a possible application of enhanced enums that Goetz provides is command line parsing in which an enum is used to represent the data types of parameters. Vicente Romero replied to Goetz's message with two more examples of where enhanced enums might be applied: "code sharing between enum constants" and "the power of sharper typing".

Goetz encourages others to provide more use cases for enhanced enums, "Please contribute others, as well as places in the JDK where code could be refactored using this feature." He concludes, "If anyone wants to experiment and offer their experience in applying (or misapplying) this feature, either to the JDK or their own codebase, that would be appreciated...."

Monday, April 10, 2017

Java Garbage Collectors: When Will G1GC Force CMS Out?

In JEPs proposed to target JDK 9 (2017/4/4), Mark Reinhold has written that JEP 291 ("Deprecate the Concurrent Mark Sweep (CMS) Garbage Collector") is one of two JEPs that "have been placed into the 'Proposed to Target' state by their owners after discussion and review". If things go well for JEP 291, it will be targeted for JDK 9.

Reinhold explains in this message why JEP 291 can still be targeted to JDK 9 at this relatively late date: "JEP 291 requires only a minuscule code change, to enable the proposed warning message to be issued. It's a JEP in the first place not because it's a risky change but to bring visibility to the plan to remove the CMS collector in the long term." As these sentences state, the JDK 9 targeted action is simply to mark the Concurrent Mark Sweep (CMS) collector as deprecated with the idea that it will be removed at some point "in the long term."

Although G1GC is the default garbage collector for JDK 9 through JEP 248, it is not always the best collector for all situations. Even the proposal to deprecate CMS acknowledges this in its "Risks and Assumptions" which states, "For some applications CMS is a very good fit and might always outperform G1."

Another recent discussion of OpenJDK jdk9-dev mailing list is titled "JEP 291: Deprecate the Concurrent Mark Sweep (CMS) Garbage Collector" contains interesting arguments for retaining CMS. Christoph Engelbert (Hazelcast) writes, "CMS+ParNew is the most commonly deployed solution and a lot of applications are optimized to the behavior of CMS." Scott Palmer writes that in his specific application, "we have found that so far the CMS collector has significantly lower maximum pause times than G1." Roman Kennke (RedHat) adds, "I'd say it's too early to talk about removing CMS. And, to be honest, I even question the move to deprecate it." Martijn Verburg (jClarity) states, "We are now constantly asked to tune G1 for customers and have found that even with our most advanced analytics (in combination with some of the common and more esoteric tuning options), we are unable to get G1 to outperform CMS for *certain* use cases. Several customers have therefore reverted to CMS and are very interested in its future (as consumers)."

This same discussion also includes reasons for deprecating CMS. Mark Reinhold's post states that JEP 291 was "posted last summer" and requests were made for a CMS maintainer, but "so far, no one has stepped up." He concludes that post, "In any case, Oracle does intend to stop maintaining CMS at some point in the not-to-distant future, and if no one ever steps up then we'll remove the code."

Jeremy Manson (Google) explains the trickiness of the current situation with G1GC and CMS:

We decided that supporting CMS in any sort of ongoing fashion should be a last resort after we try getting G1 to do what we need it to do. Our belief is that fewer collectors is better. We spent some time over the last few months coordinating with some of the folks at Oracle and experimenting to see if there were plausible ways forward with G1. We couldn't find anything obvious.

The gist of all this seems to be that many applications still depend on CMS and these applications will have a deprecation warning displayed in JDK 9. The future of the CMS garbage collector appears to be in doubt, but it would only be deprecated in JDK 9. When the CMS collector would actually be removed seems less obvious, but I assume that JDK 10 is a potential "future major release" in which CMS support could be terminated. Quoting Manson (Google) again, "The short of it is: We are still willing to contribute work to support CMS, but we want to make sure we've done our due diligence on G1 first. Our belief has been that the JDK 10 timeframe is long enough that we don't have to make this decision hurriedly."

It looks likely that Java applications using the Concurrent Mark Sweep garbage collector in JDK9 will see warning messages about the deprecation of the CMS garbage collector. When (or if) CMS won't be available at all is less obvious and depends on who is willing to continue supporting CMS.

Tuesday, March 21, 2017

Project Amber: Smaller, Productivity-Oriented Java Language Features

Brian Goetz's recent message Welcome to Amber! introduces Project Amber (part of OpenJDK and proposed originally in January). Goetz opens the message with the introduction, "Welcome to Project Amber, our incubation ground for selected productivity-oriented Java language JEPs." Goetz reiterates that Project Amber is not for discussing ideas for arbitrary potential new language features, but rather is for collecting new language features for which a JDK Enhancement Proposal (JEP) already exists ("let's keep the focus on the specific features that have been adopted").

Three JEPs are already associated with Project Amber: JEP 286 ("Local-Variable Type Inference"), JEP 301 ("Enhanced Enums"), and JEP 302 ("Lambda Leftovers"). Goetz also writes that "the 'data classes' and 'pattern matching' features, already discussed publicly are intended to be adopted by Amber when we're ready to propose JEPs on them."

Work on Project Amber will proceed on the Amber repository that is "based on the jdk10 repo."

I was enthusiastic about the announcement of Project Coin with JDK 7 and have really enjoyed using its features. I feel a similar excitement about Project Amber and look forward to using its features on a regular basis. Nicolai Parlog has written that Project Amber Will Revolutionize Java.

Friday, June 27, 2014

Next Generation Project Valhalla Proposed

Earlier this week, Brian Goetz proposed Project Valhalla on an OpenJDK mailing list. Goetz's proposal states:

In accordance with the OpenJDK guidelines, this project will provide a venue to explore and incubate advanced Java VM and Language feature candidates such as Value Types, Generic Specialization, enhanced volatiles (and possibly other related topics, such as reified generics.)

Note that Goetz's proposal should not be confused with the 1997 version of Oracle's Project Valhalla that is described as "the code name for Oracle's flexible Java development environment for building, debugging and deploying component-based applications for the network computing platform." This "other" Project Valhalla is also described in the 15 December 1997 edition of InfoWorld: "Code-named Project Valhalla, the Oracle AppBuilder for Java is slated to ship in the first quarter of 1998. The Java tool, which is based on Borland's JBuilder Java IDE, is designed for writing n-tier, thin client applications."

The "other" Project Valhalla became AppBuilder for Java and was included with JDeveloper Suite (15 April 1998). The Oracle Application Server (4.0 at the time) that was part of this same JDeveloper Suite has new company at Oracle with Oracle's acquiring WebLogic with the BEA acquisition and acquiring GlassFish with the Sun acquisition and JDeveloper is an Oracle-provided (free in the "free beer" sense, but not open source) Java IDE.

Returning to the "next generation" of Project Valhalla, the voting on this Goetz proposal closes on 7 July 2014. Several have already expressed "yes" in replies to Goetz's original e-mail post. In that forum, Patrick Wright asked, "how is this/will this be different from the Da Vinci Project/MLVM?" and John Rose answered that "the charter for Da Vinci is to incubate JVM features for languages other than Java ... or for general language support without associated Java language changes" while "Valhalla is intended to support the evolution of Java itself."

I was excited about Project Coin when it was announced and enjoyed reading and hearing about its progress, but Project Valhalla would be much more ambitious and much more exciting if it is investigated and some or all of the proposed features are implemented.