Immutable Collections in Java with Sealed Types

Author Don Raab

Designing Immutable Collection using Sealed Types in JDK 15

How to define contractual, structural, and verifiable immutable Java collections.

Introducing Sealed Types

JDK 15 was released on September 15, 2020. JEP 360 Sealed Types was included as a preview feature in this release, with its second preview in JDK 16. Sealed Types is part of Project Amber. Sealed classes or interfaces can be used to restrict the interfaces or classes that are allowed to extend them. This is accomplished by using the sealednon-sealed, and permits modifiers.

What is contractual immutability?

An interface or class is contractually immutable if the available public methods do not allow an instance to be mutated after it is constructed. A contractually immutable collection should not expose methods like addaddAllremoveremoveAllclear and a mutable Iterator .

These methods are available on the CollectionList and Set types in Java. Immutable collections that implement these interfaces are not contractually immutable.

What is structural immutability?

An object is structurally immutable if all of its data members are privatefinal, and cannot be modified after the object is constructed. String is a great example of a class in Java that is structurally immutable. Once a String is constructed, it cannot be changed. Immutable objects like String sometimes have mutable counterparts like StringBuilder.

What is verifiable immutability?

A class or interface is verifiably immutable if all of the implementations are contractually and structurally immutable, and are restricted to a specific set of classes that can be verified. This is a capability that can now be more easily achieved via Sealed Types in JDK 15. With Sealed Types a developer can restrict the implementations of interfaces and classes to a specified set of types.

A Perfect Use Case for Sealed Types in Java

Immutable collection implementations for Java are available in the Java Collections Framework (since JDK 9), Google Guava and Eclipse Collections. None of the immutable collection alternatives provide the combination of structuralcontractual and verifiable immutability today.

Java 9+

There are structurally immutable collections available in the Java Collection Framework via List.of()Set.of()Map.of(). The JDK immutable collections are not contractually immutable, because they implement the mutable ListSetMap interfaces.

Google Guava

Guava has collection types that are structurally immutable, but not contractually immutable. The immutable collections in Guava implement the mutable JDK interfaces — ListSetMap. Guava restricts the implementations of the immutable collection types by using abstract classes with package private constructors, which require all implementations to be in the same package. This restriction is a novel design approach and a key component of verifiable immutability, but is still lacking contractual immutability.

Eclipse Collections

Eclipse Collections has collection types that are both contractually and structurally immutable. Unfortunately, there is no way in Java 8 to restrict the implementations of interfaces like ImmutableCollectionImmutableListImmutableSet so that verifiable immutability can be provided. It is possible to implement the ImmutableCollection interface and its subtypes outside of Eclipse Collections because they are public interfaces. Theoretically, a “bad actor” may implement the ImmutableCollection interface and pass a mutable implementation to a method call expecting an ImmutableCollection. In practice, it is doubtful that this would be an issue, but the potential does exist.

Sealed Types

The Sealed Types preview in JDK 15 gives developers the capability to finally provide the trifecta of contractualstructural and verifiable immutability in a collections framework. Using the Sealed Types preview feature, we can restrict the implementations of an ImmutableCollection interface using the sealed and permits modifiers.

sealed interface ImmutableCollection

Similarly, we can restrict the implementations of ImmutableList.

sealed interface ImmutableList

The ImmutableEmptyList implementation of ImmutableList is then declared as final.

class ImmutableEmptyList

Experimenting with Sealed Types in JDK 15 has been interesting and encouraging. I wish this feature was available a decade ago when we first defined the ImmutableCollection hierarchy in Eclipse Collections. I’ve been able to extend the design ideas that we implemented in Eclipse Collections years ago with a feature that provides a more restrictive modeling capability.

The Deck of Cards Kata: Custom Collections

The source code for an experimental implementation of a collections framework can be found in the Deck of Cards Kata repo. The Deck of Cards Kata can be taken to become familiar with multiple collections frameworks including the latest versions of the Java Collections + Streams framework, Apache Commons Collections, Google Guava and Eclipse Collections.

The custom collections framework interfaces and implementations can be browsed online here. The following class diagram shows the interfaces in the framework, including the immutable collection interfaces that leverage Sealed Types.

A Custom Collections framework in the Deck of Cards Kata

The experimental collections framework in the kata has been evolving to use Project Amber features as they become available as preview features in the JDK. The kata was upgraded to JDK 15 the day it was released. The framework now uses the following features from Project Amber:

In addition, default methods and static interface methods are used extensively to build the rich interfaces in the framework.

A vision for the Future of Java Collections

The custom collection framework was initially developed to explore and demonstrate what it would be like to have eager methods directly on mutable collection interfaces using API names similar to Java Streams.

The intent was to use the latest features available in the most current releases of Java where they were proved useful. The latest evolution shows what is possible by leveraging Sealed Types to implement immutable collection types. I’m quite encouraged by the results of the feature so far. I hope that this use case can be used and discussed as an example of the practical applicability of the Sealed Types feature.

The following blogs explain the evolution of the custom collections framework design over the past six months.

https://medium.com/javarevisited/java-streams-are-great-but-its-time-for-better-java-collections-42d2c04235d1

https://medium.com/javarevisited/eager-is-easy-lazy-is-labyrinthine-b12605f13048

I hope you found this blog useful. Check out the source code and give the Deck of Cards kata and other code katas included in the repo a try.

Enjoy!

I am a Project Lead and Committer for the Eclipse Collections OSS project at the Eclipse FoundationEclipse Collections is open for contributions. If you like the library, you can let us know by starring it on GitHub.

This was originally posted on Medium here.

Topics:

Don’t Forget to Share This Post!

Comments (0)

Your email address will not be published. Required fields are marked *

Highlight your code snippets using [code lang="language name"] shortcode. Just insert your code between opening and closing tag: [code lang="java"] code [/code]. Or specify another language.

Related Articles

Subscribe to foojay updates:

https://foojay.io/feed/
Copied to the clipboard