4 Unnamed Classes and Instance Main Methods

The addition of instance main methods and unnamed classes to the Java language enables students to write streamlined declarations for single-class programs and then seamlessly expand their programs later to use more advanced features as their skills grow.

Two preview features, instance main methods and unnamed classes, are added to the Java language. This is an evolutionary step in the language that enables students to begin writing small programs without needing to understand the full set of language features designed for large programs. Far from being a separate dialect, students can now use the Java language to write streamlined declarations for single-class programs and later seamlessly expand their beginning programs to use more advanced features as their skills grow. Java veterans might also find instance main methods and unnamed classes features useful when writing simple Java programs that do not require programming-in-the-large scaffolding of the Java language.

Note:

This is a preview feature. A preview feature is a feature whose design, specification, and implementation are complete, but is not permanent. A preview feature may exist in a different form or not at all in future Java SE releases. To compile and run code that contains preview features, you must specify additional command-line options. See Preview Language and VM Features.

For background information about instance main methods and unnamed classes, see JEP 445.

The Java language enables development teams to create, develop, and maintain large and complex applications over many years. It is a multiparadigm language with rich features for data hiding, reuse, access control, namespace management, and modularity which allow components to be cleanly composed while being developed and maintained independently. With these features, components can expose well-defined interfaces for interaction with other components and also hide internal implementation details to permit the independent evolution of each. The object-oriented paradigm itself is designed for plugging together pieces that interact through well-defined protocols and also to abstract away implementation details. This composition of large components is called "programming in the large."

The Java language also offers many constructs useful for "programming in the small" in which everything is internal to a component. In recent years, it has enhanced its programming-in-the-large capabilities with modules and its programming-in-the-small capabilities with data-oriented programming.

The Java language is also intended to be a first programming language. When programmers first start out they do not write large programs, as part of a development team. They write small programs, alone. They have no need for encapsulation and namespaces which are useful to separately evolve components written by different people. When teaching programming, instructors start with the basic programming-in-the-small concepts of variables, control flow, and subroutines. At that stage there is no need for the programming-in-the-large concepts of classes, packages, and modules.

Consider the classic Hello, World! program that is often used as the first program for Java students:
public class HelloWorld {
  public static void main(String[] args) {
    System.out.println("Hello, World!");
  }
} 

In this first program:

  • The class declaration and the mandatory public access modifier are programming-in-the-large constructs. They are useful when encapsulating a code unit with a well-defined interface to external components, but rather pointless in this little example.
  • The String[] args parameter also exists to interface the code with an external component, in this case the operating system's shell. It is mysterious and unhelpful here, especially since it is never used.
  • The static modifier is part of Java's class-and-object model. For the novice, static is not just mysterious but harmful: To add more methods or fields that main can call and use, the student must either declare them all as static (thereby propagating an idiom which is neither common nor a good habit) or else confront the difference between static and instance members and learn how to instantiate an object.
Novice programmers encounter these programming-in-the-large constructs before they have learned about variables and control flow, and before they can appreciate the utility of programming-in-the-large constructs for keeping a large program well organized. Educators often offer the admonition, "don’t worry about that, you’ll understand it later." This is unsatisfying to them and their students. It leaves students with the enduring impression that Java is overly complicated.

The preview language features, instance main methods and unnamed classes, reduce the complexity of writing simple programs such as Hello, World! by enabling programmers to write programs without using access modifiers, static modifiers, or the String[] parameter. The introduction of programming-in-the-large constructs can be postponed by instructors until they are needed.

Flexible Launch Protocol

New programmers want to write and run computer programs, but the current Java Language Specification focuses students on defining the core Java unit of the class and the basic compilation unit, namely a source file comprised of a package declaration, followed by import declarations and one or more class declarations.

The actions of choosing the class containing the main method, assembling its dependencies in the form of a module path or a class path (or both), loading the class, initializing it, and invoking the main method with its arguments constitute the launch protocol. In the JDK, the launch protocol is implemented by the launcher as the java executable.

By allowing instance main methods, the new preview language features enhance the Java launch protocol and provide greater flexibility in the declaration of a program's entry point as follows:
  • Allows the main method of a launched class to have public, protected, or default (such as package) access.
  • If a launched class does not contain a static main method with a String[] parameter, but does contain a static main method without parameters, the launch protocol invokes that method.
  • If a launched class does not contain a static main method, but has a non-private zero-parameter constructor (such as, public, protected, or package access) and a non-private instance main method, the launch protocol constructs an instance of the class. If the class has an instance main method with a String[] parameter, the launch protocol invokes that method; otherwise, it invokes the instance main method with no parameters.

By using instance main methods, we can simplify the Hello, World! program presented in Unnamed Classes and Instance Main Methods to:
class HelloWorld { 
    void main() { 
        System.out.println("Hello, World!");
    }
}

Selecting a main Method

This is a change of behavior when launching a class.

The launch protocol chooses to invoke the first of the following methods:

  • A static void main(String[] args) method of non-private access (such as, public, protected or package) declared in the launched class

  • A static void main() method of non-private access declared in the launched class
  • A void main(String[] args) instance method of non-private access declared in the launched class or inherited from a superclass

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

Note:

If the launched class declares an instance main method, that method will be invoked rather than an inherited "traditional" public static void main(String[] args) declared in a superclass. Therefore, if the launched class inherits a "traditional" main method but another method (such as, an instance main) is selected, the JVM will issue a warning to the standard error at runtime.

If the selected main is an instance method and is a member of an inner class, the program will fail to launch.

Unnamed Classes

In the Java language, every class resides in a package and every package resides in a module. These namespacing and encapsulation constructs apply to all code; however, small programs that do not need them can omit them.

A program that does not need class namespaces can omit the package statement, making its classes implicit members of the unnamed package. Classes in the unnamed package cannot be referenced explicitly by classes in named packages. A program that does not need to encapsulate its packages can omit the module declaration, making its packages implicit members of the unnamed module. Packages in the unnamed module cannot be referenced explicitly by packages in named modules.

Before classes serve their main purpose as templates for the construction of objects, they serve as namespaces for methods and fields. We should not require students to confront the concept of classes:
  • Before they are comfortable with the basic building blocks of variables, control flow, and subroutines,
  • Before they embark on learning object orientation, and
  • When they are still writing simple, single-file programs.
Even though every method resides in a class, we can stop requiring explicit class declarations for code that does not need it — just as we do not require explicit package or module declarations for code that does not need them.

When the Java compiler encounters a source file containing a method that is not enclosed in a class declaration, it implicitly considers such methods (as well as any unenclosed fields and any classes declared in the file) to be members of an unnamed top-level class.

An unnamed class is always a member of the unnamed package. It is also final and cannot implement any interface or extend any class other than Object. An unnamed class cannot be referenced by name, so there can be no method references to its static methods. However, the this keyword can still be used, as well as method references to instance methods.

No code can refer to an unnamed class by name, so instances of an unnamed class cannot be constructed directly. Such a class is useful only as a standalone program or as an entry point to a program. Therefore, an unnamed class must have a main method that can be launched as described above. This requirement is enforced by the Java compiler.

An unnamed class resides in the unnamed package, and the unnamed package resides in the unnamed module. While there can only be one unnamed package (barring multiple class loaders) and only one unnamed module, there can be multiple unnamed classes in the unnamed module. Every unnamed class contains a main method and represents a program. Multiple such classes in the unnamed package represent multiple programs.

An unnamed class is almost exactly like an explicitly declared class. Its members can have the same modifiers (such as private and static) and the modifiers have the same defaults (such as package access and instance membership). The class can have static initializers as well as instance initializers. One key difference is that while an unnamed class has a default zero-parameter constructor, it can have no other constructor.

With these changes, we can now write Hello, World! as:
void main() {
    System.out.println("Hello, World!");
}
Because top-level members are interpreted as members of the unnamed class, we can also write the program as:
String greeting() { return "Hello, World!"; }

void main() {
    System.out.println(greeting());
}
Or, by using a field, we can write the program as:
String greeting = "Hello, World!";

void main() {
    System.out.println(greeting);
}

A source file named HelloWorld.java that contains an unnamed class can be launched with the source-code launcher as follows:
$ java HelloWorld.java

The Java compiler compiles that file to the launchable class file HelloWorld.class. In this case, the compiler chooses HelloWorld for the class name as an implementation detail. However, that name still cannot be used directly in Java source code.

At this time, the javadoc tool will fail when asked to generate API documentation for a Java file with an unnamed class because unnamed classes do not define an API that is accessible from other classes. This behavior may change in a future release.

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

Growing a Program

A Hello, World! program written as an unnamed class is more focused on what the program actually does by omitting concepts and constructs it does not need. Even so, all members are interpreted just as they are in an ordinary class.

Concepts and constructs can be added to an unnamed class as as needed by the program. An unnamed class can easily be evolved later into an ordinary class, by wrapping its declaration (excluding import statements) inside an explicit class declaration.

Eliminating the main method altogether might seem like a natural next step, but it would work against the goal of gracefully evolving a student's first Java program into a larger one as well as impose some non-obvious restrictions (see see the section Alternatives in JEP 445). Dropping the void modifier would similarly create a distinct Java dialect.