JDK-8283380 : javac should constrain more uses of preview APIs
  • Type: CSR
  • Component: tools
  • Sub-Component: javac
  • Priority: P4
  • Status: Provisional
  • Resolution: Unresolved
  • Fix Versions: 19
  • Submitted: 2022-03-18
  • Updated: 2022-04-05
Related Reports
CSR :  
Relates :  
Description
Summary
-------

Produce an error/warning for method that override/implement a preview method.

Problem
-------

Consider a method declared as a preview method in an interface I or a class C (using the unexported JDK-private annotation `@PreviewFeature`). A good example in Java 19 is the preview method `ofVirtual` declared in class `Thread`.

A class D that `implements I` or `extends C` and directly implements I's method or overrides C's method does not receive an error or warning at compile time. The lack of error or warning is a problem, because D is clearly dependent on the preview API in I or C.

Note that the interface I or the class C is not itself a preview API. (For example, in Java 19, the class `Thread` is not a preview API, despite declaring preview methods like `ofVirtual`.)

If D is an abstract class, then it can `implements I` or `extends C` _without_ directly implementing I's method or overriding C's method. In this case, no error or warning is needed. The first concrete subclass of D is on the hook to directly implement or override the preview method in I or C, and that concrete subclass is the artifact which needs an error or warning.

Once a concrete class has directly implemented or overridden the preview method in I or C (and received an error or warning for doing so), then subclasses of that concrete class do not need any kind of error or warning relating to the preview method in I or C. That is, the concrete class forms a barrier between the part of the class hierarchy that depends on the preview method, and the part of the class hierarchy that doesn't.

Solution
--------

javac will be fixed to produce errors/warnings for methods that directly implement or override a preview method.

Consider a (currently non-existing) instance preview method in Thread:

    package java.lang;
    public class Thread {
        @PreviewFeature(feature=TEST)
        public void demo() {}
    }

Now, consider the following examples:

    package java.util;
    public class Test1 extends Thread {
        @Override
        public void demo() {} //no warning or error, as it is in the same module
    }

    package test; //in a user module "m"
    public class Test2 extends Thread {
    } //no warning or error, as the "demo" method is not overridden

    package test;
    public class Test3 extends Thread {
        @Override
        public void demo() {} //a preview error when compiling without --enable-preview, a preview warning when compiling with --enable-preview, subject to further @SuppressWarnings and/or -Xlint:+/-preview
    }

    package test;
    public class Test4 extends Test3 {
        @Override
        public void demo() {} //no warning or error, as the appropriate diagnostics was already given in the super class
    }

    package test;
    public class Test5 extends Test2 {
        @Override
        public void demo() {} //a preview error when compiling without --enable-preview, a preview warning when compiling with --enable-preview, subject to further @SuppressWarnings and/or -Xlint:+/-preview
    }

If the preview method would be a reflective preview method:

    package java.lang;
    public class Thread {
        @PreviewFeature(feature=TEST, reflective=true)
        public void demo() {}
    }

then the Test3 and Test5 cases would be altered:

    package test;
    public class Test3 extends Thread {
        @Override
        public void demo() {} //a preview warning when compiling with or without --enable-preview, subject to further @SuppressWarnings and/or -Xlint:+/-preview
    }

    package test;
    public class Test5 extends Test2 {
        @Override
        public void demo() {} //a preview warning when compiling with or without --enable-preview, subject to further @SuppressWarnings and/or -Xlint:+/-preview
    }

If the preview method was static (reflective or not):

    package java.lang;
    public class Thread {
        @PreviewFeature(feature=TEST)
        public static void demo() {}
    }

there would be no preview-related errors or warnings for any subclasses. (Although there may be traditional "instance method cannot override static" errors.)


Specification
-------------

(Recall from JLS 1.5 that: "Some preview APIs are described as _reflective_ by the Java SE Platform Specification, principally in the java.lang.reflect, java.lang.invoke, and javax.lang.model packages. ... All preview APIs not described as reflective in the Java SE Platform Specification are _normal_.)

A method that directly implements or overrides a preview method outside of the module that declares the method will get either:

 - a non-suppressible compile-time error (for normal preview methods when preview mode is disabled), or
 - a suppressible preview warning (for reflective preview methods when preview mode is disabled, or normal preview methods when preview mode is enabled).
Comments
Moving to Provisional, not Approved. What happens if a subclass abstracts a Preview method? Presumably no error/warning is generated for that case. Before the request is Finalized, please also include a pointer to or description of the existing behaviors for preview warnings/errors to give more context for this change.
05-04-2022

I've added a particular example explaining what will happen for various cases that may occur in the source code. Please let me know if there's some particular information missing. Thanks!
29-03-2022

Moving to Provisional, not Approved. Please give more details on the change in behavior proposed by this CSR.
29-03-2022