Summary
-------
Update `java.lang.Class` to describe how the names of unnamed classes are modeled in core reflection.
Problem
-------
While the unnamed classes of JEP 445 do not have names at the source files, they are compiled to class files which do have names.
Solution
--------
Add a general discussion of unnamed classes to the prologue of `java.lang.Class` and add a predicate to determine whether or not a class is unnamed.
Specification
-------------
diff --git a/src/java.base/share/classes/java/lang/Class.java b/src/java.base/share/classes/java/lang/Class.java
index 64f022c4a34..2ca95dc5a3e 100644
--- a/src/java.base/share/classes/java/lang/Class.java
+++ b/src/java.base/share/classes/java/lang/Class.java
@@ -25,6 +25,9 @@
package java.lang;
+import jdk.internal.javac.PreviewFeature;
+import jdk.internal.misc.PreviewFeatures;
+
import java.lang.annotation.Annotation;
import java.lang.constant.ClassDesc;
import java.lang.constant.ConstantDescs;
@@ -188,12 +191,12 @@ import sun.reflect.misc.ReflectUtil;
*
* <h2><a id=unnamedClasses>Unnamed Classes</a></h2>
*
- * A {@code class} file representing an unnamed class is generated by
+ * 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, the various name-related methods of {@code java.lang.Class}
+ * 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.
*
@@ -205,10 +208,12 @@ import sun.reflect.misc.ReflectUtil;
* class} files.
*
* For the {@code Class} object for an unnamed class {@code
- * HelloWorld}, the methods to get the {@linkplain #getName name},
- * {@linkplain #getTypeName type name}, {@linkplain #getSimpleName
- * simple name}, and {@linkplain #getCanonicalName canonical name} all
- * return results equal to {@code "HelloWorld"}.
+ * HelloWorld}, the methods to get the {@linkplain #getName name} and
+ * {@linkplain #getTypeName type name}
+ * return results equal to {@code "HelloWorld"} while the
+ * {@linkplain #getSimpleName
+ * simple name} of such an unnamed class is the empty string and the
+ * {@linkplain #getCanonicalName canonical name} is {@code null}.
*
* @param <T> the type of the class modeled by this {@code Class}
* object. For example, the type of {@code String.class} is {@code
@@ -1742,7 +1747,7 @@ public final class Class<T> implements java.io.Serializable,
/**
* 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}.
+ * {@linkplain #isAnonymousClass() anonymous} or {@linkplain #isUnnamedClass() unnamed}.
* A {@linkplain #isSynthetic() synthetic class}, one not present
* in source code, can have a non-empty name including special
* characters, such as "{@code $}".
@@ -1755,6 +1760,9 @@ public final class Class<T> implements java.io.Serializable,
* @since 1.5
*/
public String getSimpleName() {
+ if (isUnnamedClass()) {
+ return "";
+ }
ReflectionData<T> rd = reflectionData();
String simpleName = rd.simpleName;
if (simpleName == null) {
@@ -1804,6 +1812,7 @@ public final class Class<T> implements java.io.Serializable,
* <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>
@@ -1823,6 +1832,9 @@ public final class Class<T> implements java.io.Serializable,
* @since 1.5
*/
public String getCanonicalName() {
+ if (isUnnamedClass()) {
+ return null;
+ }
ReflectionData<T> rd = reflectionData();
String canonicalName = rd.canonicalName;
if (canonicalName == null) {
@@ -1857,12 +1869,29 @@ public final class Class<T> implements java.io.Serializable,
}
}
+ /**
+ * {@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
+ */
+ @PreviewFeature(feature=PreviewFeature.Feature.UNNAMED, // change to UNNAMED_CLASSES
+ reflective=true)
+ public boolean isUnnamedClass() {
+ return isSynthetic() && PreviewFeatures.isEnabled() && isTopLevelClass();
+ }
+
+
/**