JDK-8335984 : JEP 495: Simple Source Files and Instance Main Methods (Fourth Preview)
  • Type: JEP
  • Component: specification
  • Sub-Component: language
  • Priority: P4
  • Status: Proposed to Target
  • Resolution: Unresolved
  • Fix Versions: 24
  • Submitted: 2024-07-09
  • Updated: 2024-11-06
Related Reports
Relates :  
Relates :  
Sub Tasks
JDK-8335991 :  
JDK-8335992 :  
JDK-8342936 :  
Description
Summary
-------

Evolve the Java programming language so that beginners can write their first
programs without needing to understand language features designed for large
programs. Far from using a separate dialect of the language, beginners can write
streamlined declarations for single-class programs and then seamlessly expand
their programs to use more advanced features as their skills grow. Experienced
developers can likewise enjoy writing small programs succinctly, without the
need for constructs intended for programming in the large. This is a [preview
language feature](https://openjdk.org/jeps/12).


History
-------

This feature was first proposed for preview by
[JEP 445](https://openjdk.org/jeps/445) (JDK 21) and subsequently
improved and refined by [JEP 463](https://openjdk.org/jeps/463)
(JDK 22) and [JEP 477](https://openjdk.org/jeps/477) (JDK 23). We
here propose to preview it for a fourth time, with new terminology and a revised
title but otherwise unchanged, in order to gain additional experience and
feedback.


## Goals

-   Offer a smooth on-ramp to Java programming, so that instructors can
    introduce concepts in a gradual manner.

-   Help students write basic programs in a concise manner, and grow their code
    gracefully as their skills grow.

-   Reduce the ceremony of writing other kinds of small programs, such as
    scripts and command-line utilities.

-   Do not introduce a separate dialect of the Java language.

-   Do not introduce a separate toolchain; small Java programs should be
    compiled and run with the same tools as large programs.


## Motivation

The Java programming language excels for large, complex applications developed
and maintained over many years by large teams. It has 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 their interaction with other components while hiding internal
implementation details so as to permit the independent evolution of each.
Indeed, the object-oriented paradigm itself is designed for plugging together
pieces that interact through well-defined protocols and abstract away
implementation details. This composition of large components is called
*programming in the large*.

The Java programming language is also, however, intended to be a first language.
When programmers first start out they do not write large programs, in a team —
they write small programs, alone. They have no need for encapsulation and
namespaces, 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. Making the language more welcoming to newcomers is in the interest of
Java veterans but they, too, may enjoy writing small programs more concisely,
without any programming-in-the-large constructs.

Consider the classic [*Hello,
World!*](https://en.wikipedia.org/wiki/%22Hello,_World!%22_program) example that
is often a beginner's first program:

```
public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello, World!");
    }
}
```

There is too much clutter here — too much code, too many concepts, and too many
constructs — for what the program does.

-   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
    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 not used in small
    programs such as `HelloWorld`.

-   The `static` modifier is part of the language's class-and-object model. For
    the beginner, `static` is not just mysterious, but harmful: To add more
    methods or fields to this program, the beginner 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.

-   The beginner may be further puzzled by the mysterious incantation
    `System.out.println`, and wonder why a simple function call does not
    suffice. Even in first-week programs, the beginner may be forced to learn
    how to import basic utility classes for essential functionality, and wonder
    why they could not be provided automatically.

The new programmer encounters these concepts at the worst possible time, before
they learn about variables and control flow, and when they cannot appreciate the
utility of programming-in-the-large constructs for keeping a large program well
organized. Instructors often offer the admonition, "don't worry about that,
you'll understand it later." This is unsatisfying to they and their students
alike, and leaves students with the enduring impression that the language is
complicated.

The motivation for this work is not merely to reduce ceremony. We aim to help
programmers that are new to the Java language, or to programming in general,
learn the language in a manner that introduces concepts in the right order:
Start with the fundamental programming-in-the-small concepts, such as doing
simple textual I/O and processing arrays with `for` loops, and proceed to
advanced programming-in-the-large concepts only when they are actually
beneficial and can be more easily grasped.

The motivation for this work is, moreover, not only to help beginning
programmers. We aim to help everyone who writes small programs, whether they be
students, system administrators writing command-line utilities, or domain
experts prototyping core algorithms that will eventually be used in the heart of
an enterprise-scale software system.

We propose to make it easier to write small programs not by changing the
structure of the Java language — code is still enclosed in methods, which are
enclosed in classes, which are enclosed in packages, which are enclosed in
modules — but by hiding such details until they are useful.


## Description

First, we allow `main` methods to omit the infamous boilerplate of `public
static void main(String[] args)`, which simplifies the *Hello, World!* program
to:

```
class HelloWorld {
    void main() {
        System.out.println("Hello, World!");
    }
}
```

Second, we introduce a simple form of source file that lets developers get
straight to the code, without a superfluous class declaration:

```
void main() {
    System.out.println("Hello, World!");
}
```

Third, in simple source files we automatically import a few useful
methods for console input and output, thereby avoiding the mysterious
`System.out.println`:

```
void main() {
    println("Hello, World!");
}
```

Finally, for programs that go beyond *Hello, World!* and need, for example,
basic data structures or file I/O, in simple source files we automatically
import a range of standard APIs beyond just the `java.lang` package.

These changes combine to offer an _on-ramp_, that is, a gradual incline that
merges gracefully onto the highway. As beginners move on to larger programs,
they need not discard what they learned in the early stages, but, rather, they
see how it all fits within the larger picture. As experienced developers proceed
from prototype to production, they can smoothly grow their code into components
of larger programs.


### This is a [preview language feature](https://openjdk.org/jeps/12), disabled by default

To try the examples below in JDK 24 you must enable preview features:

-   Compile the program with `javac --release 24 --enable-preview Main.java` and
    run it with `java --enable-preview Main`; or,

-   When using the [source code launcher](https://openjdk.org/jeps/330), run the
    program with `java --enable-preview Main.java`; or,

-   When using [`jshell`](https://openjdk.java.net/jeps/222), start it with
    `jshell --enable-preview`.


### Instance `main` methods

In order to write and run programs, beginners will learn about the _entry point_
of a program. The Java Language Specification (JLS) explains that [the entry
point of a Java
program](https://docs.oracle.com/javase/specs/jls/se23/html/jls-12.html#jls-12.1)
is a method called `main`:

> The Java Virtual Machine starts execution by invoking the method `main` of
> some specified class or interface, passing it a single argument which is an
> array of strings.

The JLS [further states](https://docs.oracle.com/javase/specs/jls/se23/html/jls-12.html#jls-12.1.4):

> The method `main` must be declared `public`, `static`, and `void`. It must
> specify a formal parameter whose declared type is array of `String`.

These requirements on the declaration of `main` are historical and unnecessary.
We can streamline the entry point of a Java program in two ways: Allow `main` to
be non-`static`, and drop the requirements for `public` and an array parameter.
These changes allow us to write _Hello, World!_ with no `public` modifier, no
`static` modifier, and no `String[]` parameter, postponing the introduction of
those constructs until they are needed:

```
class HelloWorld {
    void main() {
        System.out.println("Hello, World!");
    }
}
```

Assuming this program is in the file `HelloWorld.java`, we can run it directly
with the [source-code launcher](https://openjdk.org/jeps/330):

```
$ java HelloWorld.java
```

The launcher compiles `HelloWorld.java` in memory, then finds and invokes a
`main` method:

  - If the `HelloWorld` class contains a `main` method with a `String[]`
    parameter then the launcher chooses that method.

    Otherwise, if the class contains a `main` method with no parameters then the
    launcher chooses that method.

    Otherwise, the launcher reports an error and terminates.

  - If the chosen method is `static` then the launcher invokes it.

    Otherwise, the chosen method is an _instance `main` method_. The class must
    have a non-private constructor with no parameters. The launcher invokes that
    constructor and then invokes the `main` method of the resulting object. If
    there is no such constructor then the launcher reports an error and
    terminates.

Any `main` method that can be invoked under this protocol is known as a
_launchable_ `main` method. For example, the `HelloWorld` class has one
launchable `main` method, namely `void main()`.


### Simple source files

In the Java language, every class resides in a package and every package resides
in a module. Modules and packages provide namespacing and encapsulation for
classes, but small programs that consist of a few classes do not need
these concepts. Accordingly, developers can omit package and module
declarations, and their classes will reside in an unnamed package of an unnamed
module.

Classes provide namespacing and encapsulation for fields and methods.  We should
not require beginners to understand these concepts before they are comfortable
with the basic building blocks of variables, control flow, and
subroutines. Accordingly, we can stop requiring class declarations for small
programs that consist of a few fields and methods, just as we do not require
package or module declarations.

Henceforth, if the Java compiler encounters a source file with fields and
methods that are not enclosed in a class declaration, it will consider the
source file to _implicitly declare_ a class whose members are the unenclosed
fields and methods.  Such a source file is called a _simple_ source file.

With this change, we can write _Hello, World!_ as a simple source file:

```
void main() {
    System.out.println("Hello, World!");
}
```

The implicitly declared class of a simple source file

- Is a `final` top level class in the unnamed package;
- Extends `java.lang.Object` and does not implement any interfaces;
- Has a default constructor with no parameters, and no other constructors;
- Has, as its members, the fields and methods in the simple source file; and
- Must have a launchable `main` method; if it does not, a compile-time error is
  reported.

Since the fields and methods declared in a simple source file are interpreted as
members of the implicitly declared class, we can write _Hello, World!_ by calling a
method declared nearby:

```
String greeting() { return "Hello, World!"; }

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

or by accessing a field:

```
String greeting = "Hello, World!";

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

Since a simple source file declares a class implicitly, the class does not have
a name that can be used in code. We can refer to the current instance of the
class via `this`, either explicitly or, as above, implicitly, but we cannot
instantiate the class with `new`. This reflects an important tradeoff: If
beginners have not yet learned object-oriented concepts such as classes, then
writing code in a simple source file should not require a class declaration —
which is what would give the class a name usable with `new`.

Assuming our simple source file is called `HelloWorld.java`, we can run it
directly with the source-code launcher:

```
$ java HelloWorld.java
```

The launcher compiles `HelloWorld.java` in memory, treating its fields and
methods as if they are members of a class called `HelloWorld`, deriving the name
of the class from the name of the file. The launcher then finds and invokes a
`main` method, as described earlier.

If a simple source file has a launchable `main` method that is an instance
method, then running that file with the `java` launcher is equivalent to
embedding it in an [anonymous class declaration], instantiating the anonymous
class, and invoking the launchable `main` method:

```
new Object() {

    String greeting = "Hello, World!";

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

}.main();
```

The `javadoc` tool can generate documentation from a simple source file, even
though the implicitly declared class cannot be referenced by other classes and
thus cannot be used to define an API. Documenting the members of the implicitly
declared class may be useful for beginners learning about `javadoc`, and for
experienced developers prototyping code intended to be used in a larger program.


### Interacting with the console

Many beginner programs need to interact with the console. Writing to the console
ought to be a simple method invocation, but traditionally it requires using the
qualified name `System.out.println`. This is mildly painful for experienced
developers, but deeply mysterious to the beginner: What is `System`, what is
`out`, and what are the dots for?

Even worse is reading from the console which, again, ought to be a simple method
invocation. Since writing to the console involves `System.out`, it seems
reasonable that reading would involve `System.in`, but getting a `String` from
`System.in` requires all this code:

```
try {
    BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
    String line = reader.readLine();
    ...
} catch (IOException ioe) {
    ...
}
```

Experienced developers are used to this boilerplate, but for the beginner this
code contains yet more mysterious concepts, leading to a plethora of questions:
What are `try` and `catch`, what is a `BufferedReader`, what is an
`InputStreamReader`, and whatever is an `IOException`? There are other
approaches, but none is significantly better, especially for the beginner.

To simplify the writing of interactive small programs, we make five methods
available for use in simple source files:

```
public static void println(Object obj);
public static void println();
public static void print(Object obj);
public static String readln(String prompt);
public static String readln();
```

A beginner can now write *Hello, World!* as:

```
void main() {
    println("Hello, World!");
}
```

They can then easily move on to the simplest of interactive programs:

```
void main() {
    String name = readln("Please enter your name: ");
    print("Pleased to meet you, ");
    println(name);
}
```

The five `static` methods above are declared in the new class `java.io.IO`,
which is a preview API in JDK 24. Every simple source file imports these
`static` methods automatically, as if the declaration

```
import static java.io.IO.*;
```

appears at the start of every simple source file.


### Automatic import of the `java.base` module

Many other classes in the Java Platform API are useful in small programs. They
can be imported explicitly at the start of a simple source file:

```
import java.util.List;

void main() {
    var authors = List.of("James", "Bill", "Guy", "Alex", "Dan", "Gavin");
    for (var name : authors) {
        println(name + ": " + name.length());
    }
}
```

Experienced developers will find this natural, though for convenience some might
be inclined to use import-on-demand declarations (i.e., `import java.util.*`).
For beginners, however, any form of `import` is another source of mystery,
requiring an understanding of the package hierarchy of the Java API.

To further simplify the writing of small programs, we make all of the public top
level classes and interfaces of the packages exported by the `java.base` module
available for use in simple source files, as if they were imported on demand.
Popular APIs in commonly used packages such as `java.io`, `java.math`, and
`java.util` are thus immediately usable. In the example above, `import
java.util.List` can be removed since `List` will be imported automatically.

A [companion JEP](https://openjdk.org/jeps/8335987) proposes a new import
declaration, `import module M`, which imports, on demand, all of the public top
level class and interfaces of the packages exported by module `M`. Every
simple source file is considered to import the `java.base` module automatically,
as if the declaration

```
import module java.base;
```

appears at the start of every simple source file.


### Growing a program

A small program in a simple source file is focused on what the program does,
omitting concepts and constructs it does not need. Even so, all members are
interpreted as in an ordinary class. To evolve a simple source file into a
ordinary source file, all we need to do is wrap its fields and methods in an
explicit `class` declaration, and add the automatic imports. For example, this
simple source file:

```
void main() {
    var authors = List.of("James", "Bill", "Guy", "Alex", "Dan", "Gavin");
    for (var name : authors) {
        println(name + ": " + name.length());
    }
}
```

can be evolved into an ordinary source file that declares a single class:

```
import static java.io.IO.*;
import module java.base;

class NameLengths {
    void main() {
        var authors = List.of("James", "Bill", "Guy", "Alex", "Dan", "Gavin");
        for (var name : authors) {
            println(name + ": " + name.length());
        }
    }
}
```

The `main` method does not change in any way. Thus turning a small program into
a class that can serve as a component in a larger program is always
straightforward.


## Alternatives

-   _Use [JShell] for small programs_ — A JShell session is not a program, but a
    sequence of code snippets. Declarations typed into `jshell` are implicitly
    viewed as static members of some unspecified class, with some unspecified
    access level, and statements execute in a context in which all previous
    declarations are in scope.

    This is convenient for experimentation — which is the primary use case for
    JShell — but not a good model for writing small programs. Evolving a batch
    of working declarations in JShell into a real program leads to a
    non-idiomatic style of code because it declares each method, class, and
    variable as `static`. JShell is a great tool for exploration and debugging,
    but it is not the on-ramp programming model we are looking for.

-   _Interpret code units as static members_ — Methods and fields are
    non-`static` by default. Interpreting top level members in a simple source
    file as `static` would change the meaning of the code units in such a class
    — introducing, in effect, a distinct Java dialect. To preserve the meaning
    of such members when we evolve a simple source file into an ordinary source
    file we would have to add explicit `static` modifiers. When we scale up from
    a handful of methods to a simple class we want to use classes as classes,
    not merely containers of static members.

-   _Interpret the simple source file as a method body_ — Rather than treat a
    simple source file as the body of an implicitly declared class, we could
    treat it as the body of a `main` method of an implicitly declared class. In
    other words, a simple source file could contain only statements, local class
    or interface declarations, or local variable declarations, without the
    header of the `main` method.

    This approach would be limiting since there would be no way to declare
    auxiliary methods; we could only write linear programs, without the power to
    abstract repeating computations into subroutines.  Moreover, there would be
    no way to declare fields; all variable declarations would be treated as
    local variable declarations. This is a limitation because local variables
    can only be accessed from inside lambda bodies or inner classes when they
    are effectively final, whereas fields do not have that constraint.

    The design we propose above supports both auxiliary methods and field
    declarations. The burden of writing a `main` method header, especially with
    the relaxed requirements on this method in a simple source file, is not
    onerous, even for beginners.

-   _Introduce package-level methods and fields_ — We could achieve a user
    experience similar to that shown above by allowing package-level methods and
    fields to be declared in a file without an explicit `package` or `class`
    declaration. However, such a feature would have a far wider impact on how
    Java code is written in general.

-   _Different automatic imports_ — Rather than have the class implicitly
    declared by a simple source file import all 54 packages in the
    `java.base` module on demand, we could instead have it import some subset of
    those packages.  But, which subset should that be?

    Every reader will have suggestions for which packages to auto-import into
    every small program: `java.io` and `java.util` would be near-universal
    suggestions; `java.util.stream` and `java.util.function` would be common;
    and `java.math`, `java.net`, and `java.time` would each have supporters. For
    the JShell tool, we managed to find ten `java.*` packages which are broadly
    useful when experimenting with one-off Java code, but it is difficult to see
    which subset of `java.*` packages deserves to be permanently and
    automatically imported into every small program. The list would, moreover,
    change as the Java Platform evolves; e.g., `java.util.stream` and
    `java.util.function` were introduced only in Java 8. Developers would
    likely become reliant on IDEs to remind them of which automatic imports are
    in effect — an undesirable outcome.

    Importing all of the packages exported by the `java.base` module is a
    consistent and reasonable choice for classes implicitly declared by simple
    source files.

-   _Introduce a new dialect of the Java language_ — A radically different
    design would define a different dialect of the language for use in simple
    source files. This would allow all sorts of things to be removed in the
    pursuit of brevity. For example, we could drop the requirement that `main`
    methods be explicitly declared `void`. Unfortunately, this would prevent the
    graceful evolution of small programs to larger ones, which is a more
    important goal. We prefer an on-ramp to a cliff edge.

[method references]:
    https://docs.oracle.com/javase/specs/jls/se23/html/jls-15.html#jls-15.13
[anonymous class declaration]:
    https://docs.oracle.com/javase/specs/jls/se23/html/jls-15.html#jls-15.9.5
[JShell]: https://openjdk.org/jeps/222
[effectively final]:
    https://docs.oracle.com/javase/specs/jls/se23/html/jls-4.html#jls-4.12.4