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.

No comments: