Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8316779 Null-Restricted Value Class Types (Preview)
  3. JDK-8317765

Java language implementation of null-restricted value class types



    • Sub-task
    • Resolution: Unresolved
    • P4
    • None
    • None
    • tools
    • None


      This task summarizes the language changes introduced by the parent JEP, JDK-8317765. All features are activated by the `--enable-preview` javac option.

      ### 'implicit' constructors

      The 'implicit' keyword is context-sensitive, recognized when used as the modifier of a constructor.

      An 'implicit' constructor must have the following shape:
      - 'public' access
      - No arguments
      - No type parameters
      - No 'throws' clause
      - No body, using ';' instead

      A 'regulated' modifier is optional and implicit.

      To ensure that a zero instance can be created without executing any code, the class of an implicit constructor:
      - Must not be an identity class (may be value or unconstrained abstract)
      - Must not be an inner class
      - Must not declare any instance field initializers or instance initializer blocks
      - Must not declare an instance field whose default value is/contains the zero instance of the current class
      - Must extend 'Object' or an abstract class with an implicit constructor

      Much like an abstract method, the 'implicit' constructor is a real API point can be invoked directly, producing the zero instance of the class.

      ### Null-restricted types

      A _null-restricted type_ is a kind of class type (and thus a reference type), expressed with a '!' suffix to a concrete value class name, possibly followed by type arguments. The value class must declare an `implicit` constructor. The type arguments, if any, must not be wildcards.

      Null-restricted types can appear most places a class type can appear:
      - As the type of a field or record component
      - As the type of a local variable or pattern variable
      - As the type of a formal parameter
      - As the type of an exception parameter
      - As the return type of a method
      - As the element type of an array type
      - As the element type of an array creation expression or method reference
      - As the type of a cast expression or instanceof expression
      - As the type to search of a method reference with a method name

      Null-restricted types are _not_ supported in a few places:
      - In an extends/implements/throws clause
      - As the class of a class instance creation expression or 'new' method reference
      - As the type of a receiver parameter
      - As the qualifying type of an inner class type
      - As a type argument
      - As a type variable bound

      The members and supported operations on a null-restricted type are the same as for a regular class type.

      ### Type checking

      A null-restricted type is not a _subtype_ of any other type. However, the type `Foo!` may be _converted_ to the type `Foo` by a _widening nullness conversion_.

      The type `Foo` may similarly be converted to the type `Foo!` by a _narrowing nullness conversion_. A narrowing nullness conversion always implies a null check, as has historically been performed by unboxing. If the check fails, the conversion throws a NullPointerException.

      Array subtyping allows for widening nullness conversion of the component type: `Foo![]` is a subtype of `Foo[]`.

      There is no conversion in the opposite direction: a `Foo[]` can never be treated as a `Foo![]`, not even with an unchecked cast. This is a unique situation in the language: once the null restriction of an array type is lost, it cannot be recovered. (Such conversions may be supported in the future.)

      Conversion contexts allow nullness conversions to be combined with reference conversions—e.g., `Object` can be cast to `Foo!` via a narrowing reference conversion followed by a narrowing nullness conversion.

      The null type is not a subtype of any null-restricted type—a compile-time error occurs when attempting to assign a `null` literal to a null-restricted variable.

      An instance creation expression for an implicitly-constructible value class has a null-restricted type. Expressions that read variables or invoke methods also have null-restricted types if the variable
      or method return is declared null-restricted.

      The erasure of a null-restricted type is the class type denoted by the value class name. As usual, erasure applies recursively to array component types. However, null-restricted types _are_ considered reifiable, assuming the underlying class type is reifiable (thus allowing for, e.g., array creations).

      Nullness conversions, in both directions, are allowed when method overriding occurs. That is, method parameters may be considered "the same" whether they use null restrictions or not; covariant returns are allowed whether the return type uses null restriction or not; the overriding method may use a standard class return type to override a null-restricted return type in the superclass. ('Foo![]', on the other hand, can be used as a covariant return, but is not considered the same parameter type as 'Foo[]'.)

      Overload resolution allows nullness conversions in strict invocation contexts. Type inference applies nullness widening conversion to any null-restricted types before attempting to infer bounds. 'lub' and 'glb' do the same, unless all inputs are the same null-restricted type or, in the case of 'glb', supertypes of the widened null-restricted type.

      ### Runtime checking

      As noted above, every narrowing nullness conversion, including in assignments, invocations, and casts, prompts a runtime null check. A null value causes a NullPointerException to be thrown.

      Array store checks may also check for null (depending on the array's underlying component `CheckedType`). If prohibited, a null value causes an ArrayStoreException to be thrown.

      In separate compilation scenarios, a null assignment to a field that was allowed without conversion at compile time may cause a FieldStoreException to be thrown at runtime.

      (Tentatively:) Any null-restricted parameters of a concrete instance method are dynamically checked for 'null' on entry to the method. This ensures the types are correct even if the null restrictions of an overridden method's parameters do not match.

      (Tentatively:) If an invoked method has a null-restricted return type, the return value is dynamically checked for 'null' on return to the call site. This ensures the types are correct even if the invoked method was overridden by another class with a non-restricted return type.

      ### Boxing and unboxing

      Because the wrapper classes are implicitly constructible, the types `Integer!`, `Double!`, etc., can be used.
      These provide more precise targets for boxing/unboxing.

      Boxing conversion is redefined to map `int` to `Integer!`, `double` to `Double!`, etc.

      Unboxing conversion is redefined to map `Integer!` to `int`, `Double!` to `double`, etc. There is no longer a null check associated with unboxing—that has been shifted to narrowing nullness conversion.

      The conversion contexts combine nullness and boxing/unboxing conversions as necessary to preserve existing behavior. (E.g., an assignment allows boxing, optionally followed by widening nullness, optionally followed by widening reference.)

      ### Compilation

      See JDK-8317766 for class file format.

      All classes are generated with the preview version number.

      Implicit constructors are compiled to a constructor body that simply calls `super` (fields will be initialized to zero values by the JVM automatically). The `ImplicitCreation` class attribute is also set.

      Null-restricted types appear in `Signature` attributes.

      Null-restricted fields and array creations get an appropriate `CheckedType`.

      Narrowing nullness conversions and other implicit null checks compile to `checkcast` with an appropriate `CheckedType` target. If a narrowing reference conversion also occurred, that may require an additional `checkcast` with a `Class` target in order to satisfy the verifier.

      ### Class file reading

      When preview features are enabled, the following classes are considered to have an implicit constructor:
      - java.lang.Number
      - java.lang.Record
      - All 8 wrapper classes
      - java.util.Optional

      ### Other language tools and APIs

      - 'javadoc' output indicates implicit constructors

      - 'javax.lang.model' is updated to support the 'implicit' modifier and null-restricted types




            dlsmith Dan Smith
            dlsmith Dan Smith
            1 Vote for this issue
            2 Start watching this issue