JDK-8319044 : Core Reflection API changes for Implicitly Declared Classes (Second Preview)
  • Type: CSR
  • Component: core-libs
  • Sub-Component: java.lang:reflect
  • Priority: P3
  • Status: Provisional
  • Resolution: Unresolved
  • Fix Versions: 22
  • Submitted: 2023-10-27
  • Updated: 2023-11-07
Related Reports
CSR :  
Relates :  
Relates :  
Description
Summary
-------

Remove reflective APIs for unnamed classes, remove documentation related to unnamed classes from javadoc and add documentation in `java.lang.Class` related to implicit classes.

Problem
-------

In the second preview of this feature, support for unnamed classes has been dropped and replaced with implicit classes. The distinction between unnamed classes and implicit classes is that the implicit class syntax is purely syntactic sugar and the classes generated by the compiler are exactly like regular classes with no indication of their origin.

Solution
--------

Remove reflective APIs that rely on the detectability of unnamed classes and remove documentation related to unnamed classes. Also add documentation to `java.lang.Class` to indicate that implicit classes are a compiler construct.

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

Documentation in `java.lang.Class` related to unnamed classes has been replaces with documentation for implicit classes.

`java.lang.Class::isUnnamedClass` is removed. This was the method used to detect an unnamed class. It is not possible to detect implicit classes since they are regular classes. `java.lang.Class::getSimpleName`, `java.lang.Class::getCanonicalName` and `java.lang.Class::isAnonymousClass` have documentation removed related to unnamed classes.

```
diff --git a/src/java.base/share/classes/java/lang/Class.java b/src/java.base/share/classes/java/lang/Class.java
index c95aa764ee6..e1893d82901 100644
--- a/src/java.base/share/classes/java/lang/Class.java
+++ b/src/java.base/share/classes/java/lang/Class.java
@@ -190,30 +190,21 @@
  * a class or interface is hidden has no bearing on the characteristics
  * exposed by the methods of class {@code Class}.
  *
- * <h2><a id=unnamedClasses>Unnamed Classes</a></h2>
- *
- * A {@code class} file representing an {@linkplain #isUnnamedClass unnamed class}
- * is generated by a Java compiler from a source file for an unnamed class.
- * The {@code Class} object representing an unnamed class is top-level,
- * {@linkplain #isSynthetic synthetic}, and {@code final}. While an
- * unnamed class does <em>not</em> have a name in its Java source
- * form, several of the name-related methods of {@code java.lang.Class}
- * do return non-null and non-empty results for the {@code Class}
- * object representing an unnamed class.
+ * <h2><a id=implicitClasses>Implicit Classes</a></h2>
  *
  * Conventionally, a Java compiler, starting from a source file for an
- * unnamed class, say {@code HelloWorld.java}, creates a
+ * implicit class, say {@code HelloWorld.java}, creates a
  * similarly-named {@code class} file, {@code HelloWorld.class}, where
  * the class stored in that {@code class} file is named {@code
  * "HelloWorld"}, matching the base names of the source and {@code
  * class} files.
  *
- * For the {@code Class} object of an unnamed class {@code
+ * For the {@code Class} object of an implicit class {@code
  * HelloWorld}, the methods to get the {@linkplain #getName name} and
  * {@linkplain #getTypeName type name} return results
  * equal to {@code "HelloWorld"}. The {@linkplain #getSimpleName
- * simple name} of such an unnamed class is the empty string and the
- * {@linkplain #getCanonicalName canonical name} is {@code null}.
+ * simple name} of such an implicit class is {@code "HelloWorld"} and the
+ * {@linkplain #getCanonicalName canonical name} is {@code "HelloWorld"}.
  *
  * @param <T> the type of the class modeled by this {@code Class}
  * object.  For example, the type of {@code String.class} is {@code
@@ -1809,7 +1800,7 @@ public Class<?> getEnclosingClass() throws SecurityException {
     /**
      * Returns the simple name of the underlying class as given in the
      * source code. An empty string is returned if the underlying class is
-     * {@linkplain #isAnonymousClass() anonymous} or {@linkplain #isUnnamedClass() unnamed}.
+     * {@linkplain #isAnonymousClass() anonymous}.
      * A {@linkplain #isSynthetic() synthetic class}, one not present
      * in source code, can have a non-empty name including special
      * characters, such as "{@code $}".
@@ -1822,9 +1813,6 @@ public Class<?> getEnclosingClass() throws SecurityException {
      * @since 1.5
      */
     public String getSimpleName() {
-        if (isUnnamedClass()) {
-            return "";
-        }
         ReflectionData<T> rd = reflectionData();
         String simpleName = rd.simpleName;
         if (simpleName == null) {
@@ -1874,7 +1862,6 @@ public String getTypeName() {
      * <ul>
      * <li>a {@linkplain #isLocalClass() local class}
      * <li>a {@linkplain #isAnonymousClass() anonymous class}
-     * <li>an {@linkplain #isUnnamedClass() unnamed class}
      * <li>a {@linkplain #isHidden() hidden class}
      * <li>an array whose component type does not have a canonical name</li>
      * </ul>
@@ -1894,9 +1881,6 @@ public String getTypeName() {
      * @since 1.5
      */
     public String getCanonicalName() {
-        if (isUnnamedClass()) {
-            return null;
-        }
         ReflectionData<T> rd = reflectionData();
         String canonicalName = rd.canonicalName;
         if (canonicalName == null) {
@@ -1931,33 +1915,12 @@ private String getCanonicalName0() {
         }
     }
 
-    /**
-     * {@return {@code true} if and only if the underlying class
-     * is an unnamed class}
-     *
-     * @apiNote
-     * An unnamed class is not an {@linkplain #isAnonymousClass anonymous class}.
-     *
-     * @since 21
-     *
-     * @jls 7.3 Compilation Units
-     */
-    @PreviewFeature(feature=PreviewFeature.Feature.UNNAMED_CLASSES,
-                    reflective=true)
-    public boolean isUnnamedClass() {
-        return PreviewFeatures.isEnabled() && isSynthetic()
-                                           && isTopLevelClass()
-                                           && Modifier.isFinal(getModifiers());
-    }
-
-
     /**
      * Returns {@code true} if and only if the underlying class
      * is an anonymous class.
      *
      * @apiNote
      * An anonymous class is not a {@linkplain #isHidden() hidden class}.
-     * An anonymous class is not an {@linkplain #isUnnamedClass() unnamed class}.
      *
      * @return {@code true} if and only if this class is an anonymous class.
      * @since 1.5
```

PDFs of changed API javadocs enclosed.


Comments
If it is not possible to detect implicit classes, how does the launcher know to check for main() rather than main(String[])/main(String...)? Moving to Provisional.
07-11-2023

Moving back to Provisional.
31-10-2023

Moving to Provisional, not Approved. Before the request is Finalized, please attached a specdiff or patch or some other more conventional representation of the API differences.
30-10-2023

Early comment: to facilitate the JCP review of the JSR 269 API (javax.lang.model and javax.annotation.processing), please file a separate issue to track those API update distinct from the core reflection issue.
30-10-2023