JDK-8244556 : Preview APIs support for sealed classes
  • Type: CSR
  • Component: core-libs
  • Sub-Component: java.lang
  • Priority: P3
  • Status: Closed
  • Resolution: Approved
  • Fix Versions: 15
  • Submitted: 2020-05-06
  • Updated: 2020-06-01
  • Resolved: 2020-06-01
Related Reports
CSR :  
Relates :  
Description
Summary
-------

Add support for _sealed classes_ in `core-libs`. Sealed classes are classes
or interfaces that restrict which other classes or interfaces may extend or
implement them.

Problem
-------

Sealed classes, see ([JEP 360](http://openjdk.java.net/jeps/360)), will be
previewed in Java SE 15 and support will be needed for them in `core-libs`.
In particular some reflection support will be needed in order to find out if a class or interface is sealed or not and to access a class' permitted subclasses if applicable

Solution
--------

Enhance `core-libs` to support sealed classes as follows. 

- Add a new predicate to `java.lang.Class` to query if the given `class` is sealed or not.

- Also add another method to `java.lang.Class` which returns an array of `java.lang.constant.ClassDesc` containing the permitted subclasses. This array can only be empty if the given `class` is not sealed. 

Specification
-------------
    /**
     * {@preview Associated with sealed classes, a preview feature of the Java language.
     *
     *           This method is associated with <i>sealed classes</i>, a preview
     *           feature of the Java language. Preview features
     *           may be removed in a future release, or upgraded to permanent
     *           features of the Java language.}
     *
     * Returns an array containing {@code ClassDesc} objects representing all the
     * direct subclasses or direct implementation classes permitted to extend or implement this class or interface
     * if it is sealed. If this {@code Class} object represents a primitive type, {@code void}, an array type,
     * or a class or interface that is not sealed, an empty array is returned.
     *
     * @return an array of class descriptors of all the permitted subclasses of this class or interface
     *
     * @jls 8.1 Class Declarations
     * @jls 9.1 Interface Declarations
     * @since 15
     */
    @jdk.internal.PreviewFeature(feature=jdk.internal.PreviewFeature.Feature.SEALED_CLASSES, essentialAPI=false)
    public ClassDesc[] permittedSubclasses() {}

    /**
     * * {@preview Associated with sealed classes, a preview feature of the Java language.
     *
     *           This method is associated with <i>sealed classes</i>, a preview
     *           feature of the Java language. Preview features
     *           may be removed in a future release, or upgraded to permanent
     *           features of the Java language.}
     *
     * Returns {@code true} if and only if this {@code Class} object represents a sealed class or interface.
     * If this {@code Class} object represents a primitive type, {@code void}, or an array type, this method returns
     * {@code false}.
     *
     * @return {@code true} if and only if this {@code Class} object represents a sealed class or interface.
     *
     * @jls 8.1 Class Declarations
     * @jls 9.1 Interface Declarations
     * @since 15
     */
    @jdk.internal.PreviewFeature(feature=jdk.internal.PreviewFeature.Feature.SEALED_CLASSES, essentialAPI=false)
    @SuppressWarnings("preview")
    public boolean isSealed() {}

Additional links
-------

* javadoc: http://cr.openjdk.java.net/~vromero/sealed_CSRs/core-libs/javadoc.00/module-summary.html
* specdiff: http://cr.openjdk.java.net/~vromero/sealed_CSRs/core-libs/specdiff.00/overview-summary.html
Comments
Adding myself as a reviewer. Moving to Approved. Discussed possible follow-up refinements that were captured in JDK-8246278.
01-06-2020

[~darcy] I have updated the spec of both method adding an explanatory note for primitive types, void and array types
01-06-2020

[~darcy] I see, then I guess that we will need to add similar wording to the permittedSubclasses method
31-05-2020

[~vromero], but the Class::isSealed specification is one of the places the primitive types and arrays could be defined as not sealed. The class objects for primitive types, void, and arrays are all created by the JVM and not derived from a class file. For this reason, various methods on java.lang.Class mention their behavior on these kinds of class objects and I think it is reasonable for Class::isSealed to follow this convention as well.
29-05-2020

[~darcy] I share your opinion on the change you propose for Class::permittedSubclasses but in the case of Class::isSealed, by definition primitives and arrays are not sealed so I'm a bit against enumerating types of classes that are not sealed, if in the future a new kind of class is added to the language then we need to remember to add it to Class::isSealed which won't be necessary if the API of the method doesn't try to enumerate the kind of classes for which the method is false. Also someone could say: well that list is incomplete, what about the wrappers of primitive classes? what about `void` which can't be classified as a primitive type? I have updated the API for both methods
29-05-2020

[~vromero], especially given the method name "permittedSubclasses", I think at least the javadoc needs to explain "permitted subclasses" means direct subclasses or direct implementors in the case of interfaces. (To recapitulate discussions from the analogous javax.lang.model change, "permittedSubtypes" may be a possible name for this method too, but it doesn't match the name of the JVM attribute.) I think it would be in keeping with discussions elsewhere in java.lang.Class to have an informative note in isSelaed like "Class objects of primitive types are array types are not sealed." If the updated JLS spec mentions annotation types as not being sealed, that would be worth a mention too.
28-05-2020

To clarify, I suggested to consider to define new APIs in `java.lang.Class` as a modernized convention `foo()` instead of `getFoo()` for two reasons: 1. Class APIs already have a mix of both conventions since Class implements TypeDescriptor.OfField `Class::arrayType`, `Class::descriptorConstable` and `Class::descriptorString` 2. `Class::getPermittedSubClasses` returns ClassDesc[] which is different than existing `getXXX` methods that returns `Class` object (for example getNestMembers) If we agree to define the new APIs in modernized convention, `Class::permittedSubclasses` can return `List<ClassDesc>`.
28-05-2020

[~darcy] I have made the following changes to the spec: - I have renamed Class::getPermittedSubclasses to Class::permittedSubclasses as suggested by Mandy in the current review - I have removed the `@throws IllegalArgumentException if a class descriptor is not in the correct format` from the specification of Class::permittedSubclasses as the method doesn't receive any argument. Now if there is any issue with the descriptors and InternalError will be thrown. But this shouldn't happen, unless there is a bug in the VM, as the VM checks the class descriptors.
21-05-2020

typo in ::isSealed javadoc /** * * {@preview ... extra * noticed
20-05-2020

I have feedback on the API that I gave in the code review. It is inappropriate for a method that takes no arguments to throw `IllegalArgumentException`. Any exception from `ClassDesc.for` should be caught and adapted into a suitable exception for this method. That said I have to question how the string coming back from the VM could be invalid? The entries in the attribute in the classfile (from which the string is determined) must be a valid CP entry for a ClassDesc_Info.
20-05-2020

I think having both Class.isSealed and Class.isNonSealed would be reasonable and each should explain that isSealed and isNonSealed can both be false. Also, if getPermittedSubclasses returns ClassDesc's, it would be helpful to have an API note describing how to properly turn those ClassDesc's into java.lang.Class instances, if desired.
09-05-2020

java.lang.reflect has always tried hard to fill the gap between the Java language and the VM representation when its possible, by example, there is a method isDefault() on j.l.r.Method even if "default" really means non abstract. As Vicente said , adding a method isDefault to j.l.r.Modifier is not possible because there is no equivalent ACC flag but i recommend adding a method isNonSealed() on j.l.Class
08-05-2020

[~darcy] regarding a isNonSealed method, we don't think that there should be one. There is no representation of non-sealedness in the class file. It would be a derived property. Regarding java.lang.reflect.Modifier kind of the same, it is true that in the JLS sealed and non-sealed are mentioned as class modifiers, but there is no equivalent for them in the class file: there is no ACC_SEALED nor ACC_NON_SEALED so I think that we shouldn't provide support for them on java.lang.reflect.Modifier, but I'm not 100% sure of this. We should probably have a broader discussion about this issue.
08-05-2020

Moving to Provisional. No updates to java.lang.reflect.Modifier? Does there need to be an isNonSealed method too? Any update to the the spec of toGenericString()?
07-05-2020