Tuesday, July 9, 2019

Signs of JDK 14 Beginning to Appear

JDK 13 is currently in Rampdown Phase 1 (RDP 1), is scheduled to enter Rampdown Phase 2 (RDP 2) in a little over one week (on 18 July 2019), and is tentatively scheduled for General Availability on 17 September 2019. What this means, of course, is that it's time to start thinking about JDK 14! This post references and summarizes some of the online resources related to JDK 14 that are starting to appear.

Project JDK 14

The main OpenJDK JDK 14 page is the best place to start when wishing to see an overview of the release and its progress. Besides a reference to its associated specification (JSR 389: "Java SE 14 Platform"), the only other information available on this page as of this writing is a simple "Status" paragraph that references development repositories and the JDK Enhancement Proposal (JEP) process.

JDK 14 Early Access Builds

JDK 14 Early Access Builds are already available! As of this writing, the latest is Build 4 (2019/7/3). Not surprisingly, the commits in the JDK 14 early access builds at this time are fixes made to earlier JDK versions that will also be available in JDK 14.

Proposed JEP 352

The Mark Reinhold e-mail message "JEP proposed to target JDK 14: 352: Non-Volatile Mapped Byte Buffers" proposes that JEP 352 ("Non-Volatile Mapped Byte Buffers") be targeted to JDK 14.

JDK 14 Speculations

It's really far too early to recklessly speculate on what might land in JDK 14, but I won't let that stop me. It seems that the two JDK 13 "preview" JEPs [JEP 354 ("Switch Expressions") and JEP 355 ("Text Blocks")] could potentially become "permanent" in JDK 14. JEP 343 ("Packaging Tool") is another that could potentially be targeted for JDK 14.

Saturday, July 6, 2019

Project Valhalla: A First Look at LW2 Inline Types

I summarized some recent Project Valhalla LW2 "inline types" progress that was made public recently in my blog post "Valhalla LW2 Progress - Inline Types." In this post, I illustrate some of the concepts summarized in that post with code examples executed against recently released Valhalla Early Access Build jdk-14-valhalla+1-8 (2019/7/4). All code examples featured in this post are available on GitHub.

The OpenJDK Wiki page "LW2" provides an illustrative example of inline types via source code for a class called "InlineType." My example makes some minor adaptions and additions to this class and is available on GitHub as a class called InlineTypeExample. Some items that stand out immediately when reviewing this source code are the presence of the keyword inline and the presence of the ? in the Comparable's generic parameter.

The source code for my adapted InlineTypeExample class has an attempt to have the inline type class extend another class commented out because that leads to a compiler error: error: Inline type may not extend another inline type or class

Similarly, that source code also has the method that attempts to set the integer field of the inline type class commented out because that will also not compile: error: cannot assign a value to final variable

With the current Valhalla LW2 build, I'm allowed to make my inline type class Serializable and it still compiles successfully.

Another GitHub-hosted illustrative class is Lw2Demonstration that compares and contrasts characteristics of the inline type class (and instances of it) to the JDK-provided java.lang.Integer class (and its instances) and to a simple custom-built Integer wrapper (and its instances). This demonstration class calls reflective methods (some which are new to the JDK 14-based Valhalla build) on the "class" types of all three things (inline type, Integer, and custom Integer wrapper) and calls some "common" methods [toString(), equals(Object), hashCode()] on instances of all three types.

Two methods are commented out in the class Lw2Demonstration because they each attempt to perform functionality on the inline type that is not supported for inline types. One of these methods attempts to synchronize on a variable of the inline type. When attempting to compile this synchronization of an inline type, the following compiler error message is seen: error: unexpected type ... required: reference ... found: InlineTypeExample

Another attempts to assign an inline type to null. When attempting to compile this, the following error message is encountered: error: incompatible types: <null> cannot be converted to InlineTypeExample

The following method from Lw2Demonstration writes out several metadata characteristics of the class types.

/**
 * Provides metadata extracted from the provided instance of
 * {@link Class} as a single {@link String}.
 *
 * @param classToInvokeInlineMethodsOn Class for which metadata
 *    is to be extracted and returned in {@link String} format;
 *    should NOT be {@code null}.
 * @return Single string representation of metadata extracted
 *    from the provided {@link Class} instance.
 * @throws NullPointerException Thrown if {@code null} is
 *    provided for my sole parameter.
 */
public static String extractClassMetadata(final Class classToInvokeInlineMethodsOn)
{
   Objects.requireNonNull("Provided Class must be non-null to extract its metadata.");

   final String className = classToInvokeInlineMethodsOn.getSimpleName();
   final String outputPrefix = "\n" + className + ".class.";
   return outputPrefix + "getName(): " + classToInvokeInlineMethodsOn.getName()
      + outputPrefix + "getSimpleName(): " + classToInvokeInlineMethodsOn.getSimpleName()
      + outputPrefix + "getCanonicalName(): " + classToInvokeInlineMethodsOn.getCanonicalName()
      + outputPrefix + "toGenericString(): " + classToInvokeInlineMethodsOn.toGenericString()
      + outputPrefix + "getTypeName(): " + classToInvokeInlineMethodsOn.getTypeName()
      + outputPrefix + "getComponentType(): " + classToInvokeInlineMethodsOn.getComponentType()
      + outputPrefix + "isInlineClass(): " + classToInvokeInlineMethodsOn.isInlineClass()
      + outputPrefix + "isIndirectType(): " + classToInvokeInlineMethodsOn.isIndirectType()
      + outputPrefix + "isNullableType(): " + classToInvokeInlineMethodsOn.isNullableType()
      + outputPrefix + "isPrimitive(): " + classToInvokeInlineMethodsOn.isPrimitive()
      + outputPrefix + " final?: " + isFinal(classToInvokeInlineMethodsOn);
}

Some of the methods invoked on the Class instance in the previous method are new to the JDK 14-based Valhalla LW2 early access build. These include isInlineClass(), isIndirectType(), and isNullableType().

The main demonstration class Lw2Demonstration creates instances of the inline type class InlineTypeExample, of JDK-provided java.lang.Integer, and of a custom wrapper for an Integer. The demonstration then runs instances of these three classes and the class definitions through the same methods and writes out the results for each so that they can be compared and contrasted. Here is the output from running this example against the Valhalla Early Access Build mentioned at the beginning of this post.

InlineTypeExample.class.getName(): dustin.examples.valhalla.lw2.InlineTypeExample
InlineTypeExample.class.getSimpleName(): InlineTypeExample
InlineTypeExample.class.getCanonicalName(): dustin.examples.valhalla.lw2.InlineTypeExample
InlineTypeExample.class.toGenericString(): public final inline class dustin.examples.valhalla.lw2.InlineTypeExample
InlineTypeExample.class.getTypeName(): dustin.examples.valhalla.lw2.InlineTypeExample
InlineTypeExample.class.getComponentType(): null
InlineTypeExample.class.isInlineClass(): true
InlineTypeExample.class.isIndirectType(): false
InlineTypeExample.class.isNullableType(): false
InlineTypeExample.class.isPrimitive(): false
InlineTypeExample.class. final?: true
InlineTypeExample: toString(): [dustin.examples.valhalla.lw2.InlineTypeExample someIntegerValue=1]
InlineTypeExample: hashCode(): 1303372796
Inline Type Example ==: true

Integer.class.getName(): java.lang.Integer
Integer.class.getSimpleName(): Integer
Integer.class.getCanonicalName(): java.lang.Integer
Integer.class.toGenericString(): public final class java.lang.Integer
Integer.class.getTypeName(): java.lang.Integer
Integer.class.getComponentType(): null
Integer.class.isInlineClass(): false
Integer.class.isIndirectType(): true
Integer.class.isNullableType(): true
Integer.class.isPrimitive(): false
Integer.class. final?: true
Integer: toString(): 1
Integer: hashCode(): 1
Integer Type Example ==: false

IntegerWrapper.class.getName(): dustin.examples.valhalla.lw2.IntegerWrapper
IntegerWrapper.class.getSimpleName(): IntegerWrapper
IntegerWrapper.class.getCanonicalName(): dustin.examples.valhalla.lw2.IntegerWrapper
IntegerWrapper.class.toGenericString(): public class dustin.examples.valhalla.lw2.IntegerWrapper
IntegerWrapper.class.getTypeName(): dustin.examples.valhalla.lw2.IntegerWrapper
IntegerWrapper.class.getComponentType(): null
IntegerWrapper.class.isInlineClass(): false
IntegerWrapper.class.isIndirectType(): true
IntegerWrapper.class.isNullableType(): true
IntegerWrapper.class.isPrimitive(): false
IntegerWrapper.class. final?: false
IntegerWrapper: toString(): dustin.examples.valhalla.lw2.IntegerWrapper@5442a311
IntegerWrapper: hashCode(): 1413653265
Integer Wrapper Example ==: false

The output shown above demonstrates some of the advertised characteristics of the inline type. The most interesting are the focus of the following table.

CharacteristicInline Type Wrapping Integerjava.lang.IntegerCustom Integer Wrapper
Inline?truefalsefalse
Indirect?falsetruetrue
Nullable?falsetruetrue
Final?truetruefalse
== Valid for Equality?truefalsefalse
toString()Implicitly CustomizedExplicitly CustomizedUses Object's
hashCode()Implicitly CustomizedExplicitly CustomizedUses Object's

To compile and execute these examples, I needed to provide the Java compiler and launcher with some special arguments. Specifically, I compiled with --enable-preview, -Xlint:preview, and -source 14. For executing the demonstration, I passed the flag --enable-preview to the Java launcher.

The updated Valhalla Early Access Build [Build jdk-14-valhalla+1-8 (2019/7/4)] provides a convenient pre-built binary for Java developers interested in trying out Valhalla LW2 prototype inline types. This post has demonstrated some of these current LW2 inline types concepts using this build. Rémi Forax has provided many more examples on GitHub (forax/valuetype-lworld).

Friday, July 5, 2019

Valhalla LW2 Progress - Inline Types

There has been significant public-facing progress in the past few weeks (June/July 2019) for Project Valhalla's LW2 phase/prototype originally proposed in October 2018. In this post, I briefly summarize some of the messages, documents, and Valhalla Early Access Builds that have been released recently. LW2 is described in the Valhalla Wiki page as an early prototype that "[moves] inline types into the existing type system."

Draft LW2 (JVM) Spec

Dan Smith announced "a first look at a preview-feature-quality spec for values/inline classes" on the valhalla-spec-experts mailing list in a message titled "Draft LW2 spec." That message sought feedback on the initial specification and has been updated based on that feedback. This is a work-in-progress, but the URL http://cr.openjdk.java.net/~dlsmith/inline-classes is currently setup to redirect to the current version of the draft specification.

Everything related to Valhalla is a work-in-progress and might change at some point, but it is interesting to read in this draft spec the direction Valhalla is currently headed from the JVM specification perspective. Here is a sample of some of the document's more interesting points (that are subject to change):

  • "This document describes changes to the Java Virtual Machine Specification, as modified by Improved Treatment of JVM Types, to support inline classes."
  • "There are five kinds of reference types: identity class types, inline class types, array types, interface types, and the Object type. Their values are references to objects."
  • "Most reference types are nullable, meaning the special null reference (representing the absence of an object) is a value of the type. Some reference types are null-free, meaning that null is not a value of the type."
  • "An identity class type names a non-inline, non-interface class defined in a class file (excluding the special class Object). Identity class types are nullable."
  • "An inline class type names an inline class defined in a class file. For each inline class, there is a nullable and a null-free inline class type; these types share the same set of values, with the exception of null."
  • "An array type consists of a component type with a single dimension (whose length is not given by the type). The component type of an array type may be boolean, any numeric type, or any reference type. Array types are nullable."
  • "An interface type names an interface defined in a class file. Interface types are nullable."
  • "The Object type names the Object class. The Object type is nullable. ... Instances of the Object class have identity but no fields, and behave like identity class instances."
  • "The default value of a nullable reference type is null. The default value of a null-free inline class type is a reference to a class instance whose fields all have the default value for their type."

OpenJDK Valhalla Wiki: L-World

The OpenJDK Valhalla Wiki page "L-World" asks and answers the questions "What Are Inline Types?" and "What is the L-World Project?" In answering those questions, the page currently describes Inline Types as "small, immutable, identity-less types" that were "formerly known as 'Value Types'" and are now known as "inline-types." The current page describes the "L-World Project" as "a series of early prototypes for bring[ing] Inline Types to the language and JDK" that "builds on work of the previous Minimal Values Types prototype (MVT)."

OpenJDK Valhalla Wiki: LW2

The OpenJDK Valhalla Wiki page "LW2" describes the LW2 prototype as "an iteration of a previous prototype, adding further language support and JDK API support for 'inline types' (formerly referred to as 'value types')." The page indicates that the LW2 prototype is based on the JDK 14 branch and the page includes several current characteristics of "inline types," some of which I list here:

  • "Inline Types may not declare an explicit super class (except Object)."
  • "All instance fields of a inline class are implicitly final."
  • "'Indirect' projections of inline types via the '?' operator."
  • "javac automatically generates hashCode, equals, longHashCode and toString computed solely from the instance's state and not from its identity"
  • "javac allows comparison of inline type using ==, !="
  • "Inline Types can not be assigned null, null can not be cast to or compared with inline types With the exception of an 'indirect' reference using the '?' operator."
  • New methods to potentially be added to java.lang.Class: isInlineClass(), asPrimaryType(), asIndirectType() / isIndirectType(), asNullableType() / isNullableType().
  • Inline types cannot be synchronized on, cannot be serialized, cannot reference themselves as direct fields, and cannot be assigned null.

Valhalla Early Access Builds

As of this writing, the Valhalla Early Access Builds page (now renamed "Project Valhalla 'L-World Inline Types' Early-Access Builds") features "Build jdk-14-valhalla+1-8 (2019/7/4)." The page opens with the paragraph, "This is an early access build, from Project Valhalla, aimed at testing a prototype implementation of L-World Inline Types." The page also mentions, "This build is based on an incomplete version of JDK 14." As is typical for early access builds, there are limitations and disclaimers regarding use of an early access build. It sounds like there are plans to periodically update these builds "with bug fixes and performance updates."

OpenJDK Valhalla Wiki: LW2 Command Line Options/p>

For developers wanting to test out the LW2 prototype, command-line options are listed in the OpenJDK Valhalla Wiki page "LW2 Command Line Options." As stated on the page itself, it "describes the command line for the experimental Java compiler, the Java launcher and HotSpot JVM for the support of L-World LW2 Inline Types in the Valhalla project."

Meeting Minutes from 19 June 2019 Valhalla Expert Group Meeting

Karen Kinnear has provided meeting minutes from the 19 June 2019 Valhalla Expert Group Meeting that provide insight into design topics on the minds of the expert group ("nullable", "flattenable", "inlinability", "specialized generics"). The notes indicate that the LW2 specification draft discussion is not intended to finalize all design decisions.

Conclusion

There's still quite a bit of work required for "inline types" (formerly "values types") to be fully introduced to Java. However, the current focus on "inline types" and the artifacts and builds associated with this concept are evidence that progress is being made in a concrete direction.

Saturday, June 15, 2019

History and Motivations Behind Java's Maligned Serialization

Issues related to Java's serialization mechanism are well-advertised. The entire last chapter of Effective Java 1st Edition (Chapter 10) and of Effective Java 2nd Edition (Chapter 11) are dedicated to the subject of serialization in Java. The final chapter of Effective Java 3rd Edition (Chapter 12) is still devoted to serialization, but includes a new item (Item 85) that goes even further emphasize two assertions related to Java serialization:

  • "The best way to avoid serialization exploits is to never deserialize anything."
  • "There is no reason to use Java serialization in any new system you write."

In the recently released document "Towards Better Serialization," Brian Goetz "explores a possible direction for improving serialization in the Java Platform." Although the main intention of this document is to propose potential new direction for Java serialization, it is an "exploratory document only and does not constitute a plan for any specific feature." This means that it is an interesting read for the direction Java serialization might take, but there is significant value in reading this document for a summary of Java serialization as it currently exists and how we got to this place. That is the main theme of the rest of my post in which I'll reference and summarize sections of "Towards Better Serialization" that I feel best articulate the current issues with Java's serialization mechanism and why we have these issues.

Goetz opens his document's "Motivation" section with an attention-grabbing paragraph on the "paradox" of Java serialization:

Java's serialization facility is a bit of a paradox. On the one hand, it was probably critical to Java's success --- Java would probably not have risen to dominance without it, as serialization enabled the transparent remoting that in turn enabled the success of Java EE. On the other hand, Java's serialization makes nearly every mistake imaginable, and poses an ongoing tax (in the form of maintenance costs, security risks, and slower evolution) for library maintainers, language developers, and users.

The other paragraph in the "Motivation" section of the Goetz document distinguishes between the general concept of serialization and the specific design of Java's current serialization mechanism:

To be clear, there's nothing wrong with the concept of serialization; the ability to convert an object into a form that can be easily transported across JVMs and reconstituted on the other side is a perfectly reasonable idea. The problem is with the design of serialization in Java, and how it fits (or more precisely, does not fit) into the object model.

Goetz states that "Java's serialization [mistakes] are manifold" and he outlines the "partial list of sins" committed by Java's serialization design. I highly recommend reading the original document for the concise and illustrative descriptions of these "sins" that I only summarize here.

  • "Pretends to be a library feature, but isn't."
    • "Serialization pretends to be a library feature. ... In reality, though, serialization extracts object state and recreates objects via privileged, extralinguistic mechanisms, bypassing constructors and ignoring class and field accessibility."
  • "Pretends to be a statically typed feature, but isn't."
    • "Serializability is a function of an object's dynamic type, not its static type."
    • "implements Serializable doesn't actually mean that instances are serializable, just that they are not overtly serialization-hostile."
  • "The compiler won't help you" identify "all sorts of mistakes one can make when writing serializable classes"
  • "Magic methods and fields" are "not specified by any base class or interface) that affect the behavior of serialization"
  • "Woefully imperative."
  • "Tightly coupled to encoding."
  • "Unfortunate stream format" that is "neither compact, nor efficient, nor human-readable."

Goetz also outlines the ramifications of these Java serialization design decisions (see the original document for more background on each of these "serious problems"):

  • "Cripples library maintainers."
    • "Library designers must think very carefully before publishing a serializable class --- as doing so potentially commits you to maintaining compatibility with all the instances that have ever been serialized."
  • "Makes a mockery of encapsulation."
    • "Serialization constitutes an invisible but public constructor, and an invisible but public set of accessors for your internal state."
  • "Readers cannot verify correctness merely by reading the code."
    • "But because serialization constitutes a hidden public constructor, you have to also reason about the state that objects might be in based on previous versions of the code."
    • "By bypassing constructors, serialization completely subverts the integrity of the object model."
  • "Too hard to reason about security."
    • "The variety and subtlety of security exploits that target serialization is impressive; no ordinary developer can keep them all in their head at once."
  • "Impedes language evolution."
    • "Complexity in programming languages comes from unexpected interactions between features, and serialization interacts with nearly everything."
    • "Serialization is an ongoing tax on evolving the language."

Perhaps my favorite section of Goetz's "Toward Better Serialization" document is the section "The underlying mistake" because the items that Goetz outlines in this section are common reasons for mistakes in other Java code I've written, read, and worked with. In other words, while Goetz is specifically discussion how these design decisions led to problems for Java's serialization mechanism, I have (unsurprisingly) found these general design decisions to cause problems in other areas as well.

Goetz opens the section "The underlying mistake" with this statement: "Many of the design errors listed above stem from a common source --- the choice to implement serialization by 'magic' rather than giving deconstruction and reconstruction a first-class place in the object model itself." I have found "magic" code written by other developers and even myself at a later date to often be confusing and difficult to reason. I've definitely realized that clean, explicit code is often preferable.

Goetz adds, "Worse, the magic does its best to remain invisible to the reader." Invisible "magic" designs often seem clever when we first implement them, but then cause developers who must read, maintain, and change the code a lot of pain when they suddenly need some visibility to the underlying magic.

Goetz cites Edsger W.Dijkstra and writes, "Serialization, as it is currently implemented, does the exact opposite of minimizing the gap between the text of the program and its computational effect; we could be forgiven for mistakenly assuming that our objects are always initialized by the constructors written in our classes, but we shouldn't have to be".

Goetz concludes "The underlying mistake" section withe a paragraph that begins, "In addition to trying to be invisible, serialization also tries to do too much. Although Goetz is writing particularly about Java's serialization currently "serializing programs [rather than] merely serializing data," I have seen this issue countless times in a more general sense. It is tempting for we developers to design and implement code that performs every little feature we think might be useful to someone at some point even if the vast majority of (or even all currently known) users and use cases only require a simpler subset of the functionality.

Given that the objective of "Towards Better Serialization" is to "explore a possible direction for improving serialization in the Java Platform," it's not surprising that the document goes into significant detail about design and even implementation details that might influence Java's future serialization mechanism. In addition, the Project Amber mailing lists (amber-dev and amber-spec-experts) also have significant discussion on possible future direction of Java serialization. However, the purpose of my post here is not to look at the future of Java's serialization, but to instead focus on how this document has nicely summarized Java's current serialization mechanism and its history.

Although the previously mentioned Project Amber mailing lists messages focus on the potential future of Java's serialization mechanism, there are some interesting comments in these posts about Java's current serialization that add to what Goetz summarized in "Toward Better Serialization." Here are some of the most interesting:

  • Goetz's post that announced "Toward Better Serialization" states that the proposal "addresses the risks of serialization at their root" and "brings object serialization into the light, where it needs to be in order to be safer."
  • Brian Goetz post reiterates through implication that big part of problems with Java's serialization today is constructing objects without invoking a constructor: "our main security goal [is to allow] deserialization [to] proceed through constructors."
  • Stuart Marks's post states, "The line of reasoning about convenience in the proposal is not that convenience itself is evil, but that in pursuit of convenience, the original design adopted extralinguistic mechanisms to achieve it. This weakens some of the fundamentals of the Java platform, and it has led directly to several bugs and security holes, several of which I've fixed personally."
    • Marks outlines some specific examples of subtle bugs in the JDK due to serialization-related design decisions.
    • Marks outlines the explicit and specific things a constructor must do ("bunch of special characteristics") that are circumvented when current deserialization is used.
    • He concludes, "THIS is the point of the proposal. Bringing serialization into the realm of well-defined language constructs, instead of using extralinguistic 'magic' mechanisms, is a huge step forward in improving quality and security of Java programs."
  • Kevin Bourrillion's post states, "Java's implementation of serialization has been a gaping wound for a long time" and adds that "every framework to support other wire formats has always had to start from scratch."

I highly recommend reading "Towards Better Serialization" to anyone interested in Java serialization regardless of whether their primary interest is Java's current serialization mechanism or what it might one day become. It's an interesting document from both perspectives.

Monday, June 10, 2019

JDK 13: VM.events Added to jcmd

CSR (Compatibility and Specification Review) JDK-8224601 ["Provide VM.events diagnostic command"] is implemented in JDK 13 as of JDK 13 Early-Access Build #24 (dated 2019/6/6) and was added via Enhancement JDK-8224600 ["Provide VM.events command"]. The CSR's "Summary" describes this enhancement: "Add a VM.events command to jcmd to display event logs." The CSR's "Solution" states, "Add a command to jcmd to print out event logs. The proposed name is 'VM.events'."

The "Problem" section of CSR JDK-8224601 explains the value achieved from adding VM.events to the already multi-functioning jcmd: "Event logs are a valuable problem analysis tool. Right now the only way to see them is via hs-err file in case the VM died, or as part of the VM.info output."

To demonstrate jcmd's new VM.events in action, I downloaded JDK 13 Early Access Build #24 and used it to compile a simple, contrived Java application that I could then run jcmd tool delivered with that same JDK 13 Early Access Build #24 against.

The first screen snapshot shown here demonstrates using jcmd to detect the PID of the simple Java application and using jcmd <pid> help to see what jcmd options are available for that particular running Java process. The presence of VM.events is highlighted.

The next screen snapshot demonstrates applying jcmd <pid> help VM.events to see the usage (including available options) for the newly added VM.events command.

The final screen snapshot demonstrates application of jcmd's new VM.events command by showing the top (most) portion of the output from running that command without any options.

The options for the VM.events command are to narrow down results to a specified log to be printed or to limit the number of events shown. By not specifying any options, I was implicitly requesting the default of all logs and all events.

In the last displayed screen snapshot, we could see that types of JVM events rendered in the output include "compilation events", "deoptimization events", garbage collection events, classes unloaded, classes redefined, and classes loaded.

I have been a big fan of jcmd for a number of years and believe it is still generally an underappreciated command-line tool for many Java developers. The addition of the VM.events command in JDK 13 makes the tool even more useful for diagnosing a wider variety of issues.

Tuesday, May 28, 2019

Google Plus's Demise Impacts Software Development

I didn't think much about it when Google announced in late 2018 "sunsetting the consumer version of Google+." Although I had a Google+ account, I didn't really use it and didn't think I'd miss it. Although I had recognized that the demise of other online resources (Google Code in 2015, Codehaus in 2015, GeoCities in 2009, Google's Knol in 2012, Dr. Dobb's in 2014) would impact my ability as a software developer to access previously published online material, I didn't consider that this could be the case for Google+. Although some of the resources mentioned earlier have left read-only content in place, it appears that Google+ content has already been removed.

The "consumer (personal) version of Google+" was shut down in early April of this year and I ran into my first link that now points only to mention of "Google+ is no longer available for consumer (personal) and brand accounts" rather than to the original content available there. The article I was attempting to read was Jean-Baptiste 'JBQ' Quéru's 2011 post "Dizzying but invisible depth."

The "Dizzying but invisible depth" post is one that that I highly recommend because it describes well how too many layers of abstraction eventually make it difficult or impossible for any one person or even any one team to understand how the entire system works. Here is one paragraph from that post:

Once you start to understand how our modern devices work and how they're created, it's impossible to not be dizzy about the depth of everything that's involved, and to not be in awe about the fact that they work at all, when Murphy's law says that they simply shouldn't possibly work.

After spending a couple of minutes trying to find an alternate version of this post, I decided to spend my time using the Wayback Machine (Internet Archive) to access the post. The Internet Archive Wayback Machine has been useful to me before and it was again this time. I was able to find a snapshot of the "Dizzying but invisible depth" post. I saved a copy of it this time.

The post I referenced here was available on the Internet Archive Wayback Machine and may be available from some other site. However, there may be other software development related resources previously hosted on Google+ that are no longer available. This is a reminder to me of the fragility of online resources. Because of this fragility, I try to save at least the links to articles and posts of interest to me so that I have a chance of accessing the article or post via the Internet Archive Wayback Machine if the original site goes away. In the case of particularly useful posts and articles, I will sometimes go as far as saving it to a PDF for future reference.

Tuesday, May 21, 2019

Explicit No-Arguments Constructor Versus Default Constructor

Most developers new to Java quickly learn that a "default constructor" is implicitly created (by javac) for their Java classes when they don't specify at least one explicit constructor. Section 8.8.9 of the Java Language Specification succinctly states, "If a class contains no constructor declarations, then a default constructor is implicitly declared." That section further describes characteristics of the implicitly created default constructor including it having no parameters, having no throws clause, and invoking the constructor of its super class that similarly accepts no arguments. A Java developer can choose to explicitly implement a no-arguments constructor that is similar to the default constructor (such as accepting no arguments and having no throws clause). In this post, I look at some reasons a developer might decide to implement an explicit no-arguments constructor rather than relying on the implicit default constructor.

Some Reasons to Explicitly Specify No-Arguments Constructors

Preclude Instantiation of a Class

A common reason for implementing an explicit no-arguments constructor is to preclude the default constructor from being implicitly created with public accessibility. This is an unnecessary step if the class has other explicit constructors (that accept parameters) because the presence of any explicit constructor will prevent the implicit default constructor from being generated. However, if there is no other explicit constructor present (such as in a "utility" class with all static methods), the implicit default constructor can be precluded by implement an explicit no-arguments constructor with private access. Section 8.8.10 of the Java Language Specification describes use of all private explicit constructors to prevent instantiation of a class.

Force Class Instantiation via Builder or Static Initialization Factory

Another reason to explicitly implement a private no-arguments constructor is to force instantiation of an object of that class via static initialization factory methods or builders instead of constructors. The first two items of Effective Java (Third Edition) outline advantages of using static initialization factory methods and builders over direct use of constructors.

Multiple Constructors Required Including No-arguments Constructor

An obvious reason for implementing a no-arguments constructor that might be as common or even more common than the reason discussed above is when a no-arguments constructor is needed, but so are constructors that expect arguments. In this case, because of the presence of other constructors expecting arguments, a no-arguments constructor must be explicitly created because a default constructor is never implicitly created for a class that already has one or more explicit constructors.

Document Object Construction with Javadoc

Another reason for explicitly implementing a no-arguments constructor rather than relying on the implicitly created default constructor is to express Javadoc comments on the constructor. This is the stated justification for JDK-8224174 ("java.lang.Number has a default constructor") that is now part of JDK 13 and is also expressed in currently unresolved JDK-8071961 ("Add javac lint warning when a default constructor is created"). Recently written CSR JDK-8224232 ("java.lang.Number has a default constructor") elaborates on this point: "Default constructors are inappropriate for well-documented APIs."

Preference for Explicit Over Implicit

Some developers generally prefer explicit specification over implicit creation. There are several areas in Java in which a choice can be made between explicit specification or the implicit counterpart. Developers might prefer an explicit no-arguments constructor over an implicit constructor if they value the communicative aspect or presumed greater readability of an explicit constructor.

Replacing Default Constructors with Explicit No-Arguments Constructors in the JDK

There are cases in the JDK in which implicit default constructors have been replaced with explicit no-arguments constructors. These include the following:

  • JDK-8071959 ("java.lang.Object uses implicit default constructor"), which was addressed in JDK 9, replaced java.lang.Object's "default constructor" with an explicit no-arguments constructor. Reading the "Description" of this issue made me smile: "When revising some documentation on java.lang.Object (JDK-8071434), it was noted that the class did *not* have an explicit constructor and instead relied on javac to create an implicit default constructor. How embarrassing!"
  • JDK-8177153 ("LambdaMetafactory has default constructor"), which was addressed in JDK 9, replaced an implicit default constructor with an explicit (and private) no-arguments constructor.
  • JDK-8224174 ("java.lang.Number has a default constructor"), which is planned for JDK 13, will replace java.lang.Number's implicit default constructor with an explicit no-arguments constructor.

Potential javac lint Warning Regarding Default Constructors

It is possible that one day javac will have an available lint warning to point out classes with default constructors. JDK-8071961 ("Add javac lint warning when a default constructor is created"), which is not currently targeted for any specific JDK release, states: "JLS section 8.8.9 documents that if a class does not declare at least one constructor, the compiler will generate a constructor by default. While this policy may be convenient, for formal classes it is a poor programming practice, if for no other reason that the default constructor will have no javadoc. Use of a default constructor may be a reasonable javac lint warning."

Conclusion

Relying on default constructors to be created at compile time is definitely convenient, but there are situations in which it may be preferable to explicitly specify a no-arguments constructor even when explicit specification is not required.