JDK-8306113 : Implementation for JEP 445: Unnamed Classes and Instance Main Methods (Preview)
  • Type: CSR
  • Component: tools
  • Sub-Component: javac
  • Priority: P3
  • Status: Closed
  • Resolution: Approved
  • Fix Versions: 21
  • Submitted: 2023-04-17
  • Updated: 2023-11-08
  • Resolved: 2023-06-03
Related Reports
CSR :  
Relates :  
Relates :  
Relates :  
Relates :  
Description
Summary
-------

Add flexible main methods and anonymous main classes to the Java language

Problem
-------

The Java language necessitates the explicit use of some programming-in-the-large constructs such as classes and access control even for the simplest of programs. This makes the language unnecessarily difficult to learn for programming beginners, and adds too much noise to small programs.

Solution
--------

This change adds two features: 

1. Flexible main methods allow the main entry point to a program to allow different signatures. In particular, they allow a main method to have no parameters, they allow a main method to have any non-private access, and they allow it to be an instance method. An instance main method can be invoked by the launcher if the launched class has a non-private no-args constructor.

2. An Anonymous Main Class is an unnamed class that is implicitly provided when a Java file declares one or more methods outside a class declaration. An anonymous main class must have a main method.

Additionally, we deprecate the "inheritance" of today's `public static void main(String[])` and have the launcher emit a warning when the launched class's entry point is such a main method declared in a superclass.

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

See JEP 445: https://openjdk.org/jeps/445

JLS spec change: https://cr.openjdk.org/~gbierman/jep445/jep445-20230515/specs/unnamed-classes-instance-main-methods-jls.html

The change has no interaction with the JNI invocation API.

The specification of the `Main-Class` attribute in the JAR File Specification will change "class name" to "binary class name"; this is not a change of behavior (see https://bugs.openjdk.org/browse/JDK-8308014):

> Main-Class: Indicates the binary name of the main application class which the launcher will load at startup. The value of this attribute must not have the `.class` extension appended to the binary name.

(The proposed JLS change in §13.1 says: "The binary name of an unnamed top level class (7.3) is any valid identifier (3.8). In simple implementations of the Java SE Platform, where compilation units are stored in files, the binary name of an unnamed top level class would typically be the name of the file containing the unnamed top level class compilation unit (7.3) minus any extension")

The `javadoc` tool will fail when asked to generate API documentation for a Java file with an unnamed class, as unnamed classes do not define any API accessible from other classes.

The `Class.isSynthetic` method returns `true` for an unnamed class.

### Selecting a main method

When launching a class, the launch protocol chooses the first of the following methods to invoke:

1. A `static void main(String[] args)` (or `String... args`) method of non-private access (i.e., `public`, `protected` or package) access declared in the launched class,

2. A `static void main()` method of non-private access declared in the launched class,

3. A `void main(String[] args)` (or `String... args`) instance method of non-private access declared in the launched class or inherited from a superclass, or, finally,

4. A `void main()` instance method of non-private access declared in the launched class or inherited from a superclass.

Note that this is a change of behavior: A `public static void main(String[] args)` -- a "traditional" main method -- is not invoked if it is inherited by the launched class. If the launched class inherits a "traditional" main method but the class successfully launches because another `main` method (i.e. a declared static no-arg `main` or an instance `main`) is found, the JVM will issue a warning to the standard error at runtime.
Comments
Unnamed class is LR(k), but no more so than implemented for lambdas. In a sense the deep lookup was only done to avoid complex error recovery and a significant rewrite of existing tests. These tests have to work for both preview and older source levels. The future plan is to reduce the size of k and update the 150+ tests to reflect changes.
05-06-2023

Moving to Approved.
03-06-2023

For future iterations of this work, I encourage consideration of a different term for the "unnamed classes", perhaps "implicit classes", but something other than "unnamed." The classes very much have names in more than a technical sense, like anonymous classes, as the name of the resulting class is commonly used to launch the program, etc. An "implicit class" also has an easier story with reflective APIs like core reflection. java.lang.Class already has getName(), getSimpleName(), getTypeName(), getCanonicalName(), which includes discussion of "binary names" vs "type names as represented in class files." It is awkward to explain that an unnamed class has each of these kinds of names and it is the same name you would get following the usual conventions, Foo.java compiling down to Foo.class, etc. [~rpressler] and [~jlaskey], please look over the various name methods in java.lang.Class to see how they should be updated to cover unnamed classes. It might be more concise to add a paragraph at the top of spec of java.lang.Class describing the expected naming behavior for unnamed classes.
24-05-2023

[~darcy] Updated spec uploaded. Deals with inner class problem spotted by Maurizio. Other changes from the reviews of Dan and Alex also incorporated.
24-05-2023

I'm not sure I agree re. ACC_MANDATED vs. ACC_SYNTHETIC. For me, the most idiomatic case of an ACC_MANDATED member is e.g. an implicit record constructor, or a record getter, or the `values` method of an Enum. These are methods that are not declared in the source - but are available to **developers** - e.g. they can rely on such members as if they wrote themselves explicitly. This is not the case with an unnamed class. An unnamed class cannot be referenced to from other classes in the same compilation units (or from a different compilation unit). For all intents and purposes it's as if the class did not exist. Then of course core reflection will return something if you do Class::forName and happen to provide the binary name of the unnamed class (which is implementation dependent). So, perhaps ACC_MANDATED still has a place because the spec says that a wrapper class needs to be generated, but I believe it's not an either/or situation: this is still the case of an ACC_SYNTHETIC member that, as other synthetic members we do not want exposed to developers (e.g. its symbol should not be available in the "symbol table" formed during compilation).
23-05-2023

Note for future iterations of this work, since the spec changes in JLS 7.3 describe the unnamed class (and its constructors, etc.) as _implicitly declared_, they would be better marked as ACC_MANDATED rather than ACC_SYNTHETIC in the class file. That would require addressing the following limitation: "It is a limitation of the class file format that only formal parameters and modules can be flagged as ACC_MANDATED (§4.7.24, §4.7.25) to indicate that, despite being compiler-generated, they are not considered implementation artifacts. There is no way to flag other compiler-generated constructs so that they too are not considered implementation artifacts (JLS §13.1). This limitation means that reflective APIs of the Java SE Platform may not accurately indicate the "mandated" status of such constructs." https://docs.oracle.com/javase/specs/jvms/se20/html/jvms-4.html#jvms-4.7.8
23-05-2023

Moving back to Provisional until the details of the reflective support are worked out and any other open spec issues are resolved.
23-05-2023

Synthetic and top level.
23-05-2023

Members of an unnamed class, of any access, are more akin to private members from an API perspective because they cannot be accessed by any other class and so don't offer any interface, in the sense of an API, to anyone (except reflectively). An unnamed class cannot be extended, it cannot extend other classes, and it cannot implement any interface. Having said that, we may want to think of a way to generate some Javadoc for it -- but not in this first preview. The synthetic bit is, indeed, not reserved for unnamed classes, but so far we don't see a need to add a specific reflective method querying whether or not the class is unnamed. If such a need arises, we could add such a method. That will require adding another class attribute, as the JVM doesn't currently know that a class is unnamed; its only distinguishing feature is that it's synthetic (because we didn't find a need for that either, at this time).
23-05-2023

[~rpressler], the javadoc tool can be run in a way that the documentation for private or protected elements is generated as well as the default of public elements. Out of the box, I don't know how it would handle such elements on an unnamed class. The synthetic bit on classes is not reserved for marking unnamed classes so would not be a reliable way of indicating whether or not a class is synthetic. Language change efforts are generally responsible for making the needed javax.lang.model updates (e.g. JDK-8307007), but I can assist here if desired.
23-05-2023

Next round of comments: At least small updates and needed to javax.lang.model to support this JEP. I've outlined my initial impression of what should be done in JDK-8308613. While the latest JLS changes do address this point explicitly, I'd like the CSR to explicitly state "varags main methods are fine too and play the same way." Question: what is running the javadoc tool on an unnamed class expected to do? Question: verifying there is no explicit new JVM-level attribute to mark an unnamed class? And no call for a method like Class.isUnnamed()? Code review comment: presumably a record class or an enum class can also define any of the new (or old) main methods. I think the testing should be augmented to cover those cases.
23-05-2023

[~darcy] The CSR now explicitly mentions varargs in `main` and also states: 1. The `javadoc` tool will fail when asked to generate API documentation for a Java file with an unnamed class, as unnamed classes do not define any API accessible from other classes. (this behaviour may change in a future release) 2. The `Class.isSynthetic` method returns true for an unnamed class (no `isUnnamed` method is added). Just to verify, do you plan to implement JDK-8308613 yourself?
23-05-2023

[~gbierman], I assume the intent is to Finalize the request with the updated spec.
20-05-2023

[~darcy] Spec updated to match the revised launch protocol in the updated JEP
19-05-2023

Moving back to Provisional in anticipation of spec updates.
18-05-2023

Moving back to Provisional until a full complement of specs are included.
18-05-2023

The change to the JAR File spec to use "binary name" looks fine.
17-05-2023

The proposed JLS change says that an unnamed class does have a binary name. Both the launcher and a JAR's `Main-Class` attribute already refer to binary names, but to clarify that I think all that's required is adding the word "binary" in the specification of the `Main-Class` attribute.
16-05-2023

While the JEP and material with a target audience of student programmers will no doubt give multi-release JAR files a wide berth, part of the CSR process is identifying and flushing out any unexpected interactions and sharp corners for all programmers.
16-05-2023

MR JARs will just work, meaning an unnamed class can be put in the versioned section of a JAR file (META-INF/versions/21). If you really want, you can set the JAR file attribute "Main-Class" to be an unnamed class, put the unnamed class in a versioned section, and `java --enable-preview -jar app.jar` will work. MR JARs are for advanced library developers that target a range of JDK releases and make use of APIs and features that aren't in the lowest version that the library targets. This is a million miles away from the student starting out. Alex has created JDK-8308014 suggesting that the JAR File spec should be clarified to say that value of the Main-Class attribute should be a named class. I don't know if this will be done as part of JEP 445 or later. It could be expanded to also recommend against putting unnamed classes in a versioned section of the JAR file.
16-05-2023

As we've raised standards and expectation for precision and details in the API specifications over time, areas that aren't been updated for many years may not conform to current conventions. While a large refactoring/rewrite of the jar file spec may be out of bounds for this project, I do think the jar spec needs to be explicitly updated to describe how they are expected to work with this feature. Also, are there any special considerations related to multi-release jar files?
16-05-2023

[~darcy] I've added a JVMS change document, and also made some cosmetic changes to the JLS version (also attached).
15-05-2023

The JAR File spec only minimally specifies executable JAR files. The Main-Class attribute in the main section of the JAR file manifest must name the main class, the spec is otherwise silent on the "launch protocol" or the signature of the main method. If an unnamed class is packaged into a JAR file as the main class then the compiler generated name will need to be known in order to create the Main-Class attribute. The JAR File spec does need an overhaul (future work). It could be updated to document that the value of the Main-Class is the compiled generated name when the main class is an unnamed class but doesn't seem like something to encourage (different set of users).
14-05-2023

A few questions: Doesn't JVMS section 5.2 need some update for the work? https://docs.oracle.com/javase/specs/jvms/se20/html/jvms-5.html#jvms-5.2 Shouldn't the jar file spec get some update? https://download.java.net/java/early_access/jdk21/docs/specs/jar/jar.html
12-05-2023

Verified that unnamed class can be executed from a jar in the same way a named class can.
08-05-2023

Moving back to Provisional; [~rpressler] please have one or more engineers review the CSR before re-Finalizing it.
06-05-2023

Attached spec in pdf format
04-05-2023

For its archival purpose, the CSR must be stand-alone. While links to documentations are a convenient supplement, [~rpressler] please attach to the CSR some representation of language changes.
04-05-2023

Final JLS spec diff linked.
04-05-2023

[~darcy]: I added a mention of the `Main-Class` manifest attribute
27-04-2023

Moving to Provisional, not Approved, contingent on the Main-Class and Jar file interactions being explicitly discussed before the CSR is Finalized.
24-04-2023

Attached WIP spec. The class named in the Main-Class attribute of an executable JAR can make use of one of the new main methods. The change has no interaction with the JNI invocation API.
21-04-2023

Moving back to Draft. Some kind of in-progress or interim iteration of the JLS changes should be attached to or included in the CSR for evaluation. As a more specific comment, it is current accepted -- and and idiom I commonly use -- for the main method to use varags, e.g. "public static void main(String... args)". I think it is worth mentioning in the JEP that this syntax is valid and can continue to be used, if desired. What interactions, if any, are there between this feature and the main-class attribute of jar files (https://docs.oracle.com/en/java/javase/20/docs/specs/man/jar.html)? I assume this is implemented via changes to the launcher rather than updates to the JNI invocation API. Whether or not the invocation API is updated should be stated explicitly in the CSR.
21-04-2023