days
-1
-6
hours
-1
-4
minutes
-2
-8
seconds
-5
0
search
My data types are sealed

JEP 360: Sealed Types

Chris Stewart
JEP 360
© Shutterstock / Giamportone

A new Java enhancement proposal, JEP 360, has graduated from being a simple draft. It proposes to bring sealed types to Java, allowing developers to impose restrictions on which other classes or interfaces may extend or implement them. Sealed types could work in tandem with records, which is the business of its older sibling, JEP 359. Let’s take a closer look at the future of Java.

In April 2019, Java Language Architect Brian Goetz submitted a JEP draft proposing two new features be added to Java. Now, the draft is all grown up and we have two new JEPs; JEP 359 and JEP 360. In this article we will focus on JEP 360 Records (Preview).

JEP 360: Sealed Types

JEP 360 aims to bolster Java by introducing sealed types. These are described by Brain Goetz as “classes or interfaces that impose restrictions on which other classes or interfaces may extend or implement them.”

There are two goals to this proposal. One: to restrict which classes can be a subtype. Two: sealed types can allow “exhaustiveness analysis at the use-site, such as when switching over type patterns for an instance of a sealed type.”

The proposed syntax has an optional permits clause and looks like this:

sealed interface Node
     permits A, B, C { ... }

By including an explicit permits clause, Node may be extended only by the types listed in the clause, which must also be members of the same module or, if in the unnamed module, the same package. Goetz notes that this may be overkill in many cases because if all the subtypes are declared in the same compilation unit, there’s no need for the permits clause because in this instance, the compiler infers a permits clause by enumerating the subtypes declared in the same compilation unit.

Subclasses and lambdas of a sealed type cannot be anonymous, and abstract subtypes of sealed types are implicitly sealed unless explicitly declared using the non-sealed modifier. Concrete subtypes of sealed types are implicitly final unless explicitly declared otherwise. Sealing, like finality, is enforced by the language compiler as well as the JVM.

Motivation, goals & non-goals

Goetz writes the following in JEP 360:

Java’s type system allows us to answer questions such as “Is a Circle a kind of Shape?”, but gives considerably less help with questions such as “What are the kinds of Shape?” The type system is no help at all when it comes to a library restricting which classes can implement a library’s types; this means that even if the design intent of the library’s author is that a given type only has a specific set of subtypes, this cannot be enforced, and therefore cannot be assumed by the implementation. (Access control allows the library author to constrain which packages can access and therefore implement the library, but cannot distinguish an implementor from a user.) Brian Goetz

The aim is to be able to catch such statements in the type system as “A Shape is either a Circle or a Rectangle”, as it not only provides useful documentation for others users, but also allows the Java compiler to perform better type checking, since it can exhaustively enumerate the known subtypes. It would also allow library authors to predict and understand the behavior of known implementations. Sealed types are also a useful source of exhaustiveness information for pattern matching.

What JEP 360 does not aim to do is provide new forms of access control like “friends”, or provide granular control over member overriding.

Now, let’s take a more detailed look at sealed types.

Grammar

JEP 360 provides the following example of sealed type grammar in action:

NormalClassDeclaration:
  {ClassModifier} class TypeIdentifier [TypeParameters]
  [Superclass] [Superinterfaces] [PermittedSubclasses] ClassBody</pre>

ClassModifier:
  (one of)
  Annotation public protected private
  abstract static sealed final non-sealed strictfp

PermittedSubclasses:
  permits ClassTypeList

ClassTypeList:
  ClassType {, ClassType}

Restrictions

Three restrictions are listed by Goetz. It’s an error if the non-sealed modifier appears on a type when none of its supertypes are sealed, it’s an error if more than one of final, sealed, or non-sealed appears on any type declaration, and it’s an error if the permits clause appears on a type that is not declared sealed.

Compiled form of a sealed type

The class file of a sealed type must have a PermittedSubtypes attribute, which lists the permitted subtypes. “If the sealed type’s declaration in source code lacks an explicit permits clause then the set of permitted subtypes is computed at compile time to be those subtypes of the sealed type which are declared in the same compilation unit as the sealed type.” The following example is offered:

PermittedSubtypes_attribute {
    u2 attribute_name_index;
    u4 attribute_length;
    u2 permitted_subtypes_count;
    u2 classes[permitted_subtypes_count];
}

SEE ALSO: JEP 359: Records

Reflection API

The JEP further notes that it would add the following public methods to java.lang.Class:

  • java.lang.constant.ClassDesc<?>[] getPermittedSubtypes()
  • boolean isSealed()

The method getPermittedSubtypes() returns an array containing java.lang.constant.ClassDesc objects representing all the permitted subtypes of this class if it is sealed, and returns an empty array if the class is not sealed.

The method isSealed returns true if the given class or interface is sealed.

Alternatives

JEP 360 points out that some languages have direct support for algebraic data types, and that a variant of enum could in fact be a more familiar way for Java developers to deal with them. However, this would not be suitable for all use cases, such as those where sums range over classes in more than one compilation unit, or sums that range over classes that are not products. As such, the slight unfamiliarity is a small price to pay for a more well-rounded feature.

Classes also support state validation through their constructors, whereas tuples don’t. And classes can have behavior related to their state, which tuples, as raw data, cannot offer.

Dependencies

As mentioned at the beginning of this article, JEP 359 and JEP 360 arrived together because they are both born of the same JEP draft. As such, due to the fact that records can work hand-in-hand with sealed types to form something called algebraic data types, JEP 359 is listed as a dependency.

For more information, check out JEP 360 in all its glory.

Author
Chris Stewart
Chris Stewart is an Online Editor for JAXenter.com. He studied French at Somerville College, Oxford before moving to Germany in 2011. He speaks too many languages, writes a blog, and dabbles in card tricks.

Leave a Reply

1 Comment on "JEP 360: Sealed Types"

avatar
400
  Subscribe  
Notify of
gniche
Guest

Is this something that anyone wants? Goetz clearly has too much time on his hands. What happened to reifiable generic types, primitive generic types, immutable arrays, better null handling, amongst other things? this goes under the same umbrella of things developers don’t want or need like jigsaw.