JDK-8344699 : JEP 512: Compact Source Files and Instance Main Methods
  • Type: JEP
  • Component: specification
  • Sub-Component: language
  • Priority: P4
  • Status: Integrated
  • Resolution: Unresolved
  • Fix Versions: 25
  • Submitted: 2024-11-21
  • Updated: 2025-05-02
Related Reports
Duplicate :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Sub Tasks
JDK-8344706 :  
JDK-8344707 :  
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.


History
-------

This feature was first proposed for preview by [JEP 445] (JDK 21) and
subsequently improved and refined by [JEP 463] (JDK 22),
[JEP 477] (JDK 23), and [JEP 495] (JDK 24). We here propose
to finalize the feature in JDK 25, renaming _simple_ source files to
_compact_ source files, with several minor improvements based upon experience
and feedback:

- The new `IO` class for basic console I/O is now in the `java.lang` package
  rather than the `java.io` package. Thus it is implicitly imported by every 
  source file. 

- The `static` methods of the `IO` class are no longer implicitly imported into
  compact source files. Thus invocations of these methods must name the class,
  e.g., `IO.println("Hello, world!")`, unless the methods are explicitly
  imported.

- The implementation of the `IO` class is now based upon `System.out` and
  `System.in` rather than the `java.io.Console` class.


## Goals

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

-   Help students write simple 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 by large teams over many years. 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 and, at the same time,
hide internal implementation details so as to permit the independent evolution
of each. Indeed, the object-oriented paradigm is, fundamentally, about plugging
together components that interact through well-defined protocols while
abstracting 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!] 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 code
    with a well-defined interface to external components, but pointless in this
    little example.

-   The `String[] args` parameter also exists to connect the code to 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
enterprise-scale software systems.

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 _compact_ 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, we add a new class in the `java.lang` package that provides basic
line-oriented I/O methods for beginners, thereby replacing the mysterious
`System.out.println` with a simpler form:

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

Finally, for programs that go beyond *Hello, World!* and need, for example,
basic data structures or file I/O, in compact source files we automatically
import a range of standard APIs beyond 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.


### Instance `main` methods

In order to write and run programs, beginners will learn about the _entry point_
of a program. The current Java Language Specification (JLS) explains that the
entry point of a Java program is a method called `main` ([§12.1]):

> 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 ([§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!");
    }
}
```

<a id="launch"/> 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
```

Alternatively, we can compile it explicitly and then run it:

```
$ javac HelloWorld.java
$ java HelloWorld
```

Either way, the launcher starts the Java Virtual Machine and then chooses and
invokes a `main` method of the specified class:

1.  If the class declares or inherits a `main` method with a `String[]`
    parameter then the launcher chooses that method.

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

    Otherwise, the launcher reports an error and terminates.

2.  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 chosen `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 chosen and invoked under this protocol is known as
a _launchable_ `main` method. For example, the `HelloWorld` class has one
launchable `main` method, namely `void main()`.

### Compact 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 just 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, but small
programs that consist of just a few fields and methods do not need these
concepts. 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 _compact_ source file.

With this change, we can write *Hello, World!* as a compact source file:

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

The implicitly declared class of a compact 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 compact 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 compact 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);
}
```

A compact source file declares a class implicitly, so the class does not have a
name that can be used in source code. The Java compiler generates a class name
when compiling a compact source file, but that name is implementation-specific
and should not be relied upon in any source code — not even source code in the
compact source file itself.

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 the `new`
operator. This reflects an important tradeoff: If beginners have not yet learned
object-oriented concepts such as classes, then writing code in a compact source
file should not require a class declaration — which is what would give the class
a name usable with `new`.

A compact source file is just another single-file source-code program. As [shown
earlier](#launch), we can run a compact source file directly with the
source-code launcher, or we can compile it explicitly and then run it.

The `javadoc` tool can generate documentation from a compact source file, even
though the implicitly declared class should not 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 larger
programs.


### Interacting with the console

Beginners frequently write programs that interact with the console. Writing to
the console ought to be straightforward, but traditionally it requires calling
the inscrutable `System.out.println` method. To beginners, this is deeply
mysterious: What is `System`? What is `out`?

Even worse is reading from the console which, again, ought to be a
straightforward method call. Since writing to the console involves using
`System.out`, it seems reasonable that reading would involve using `System.in`,
but getting a `String` from `System.in` requires a lot of code, such as:


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

Experienced developers are used to this sort of boilerplate, but for beginners
this code contains yet more mysteries, 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 programs, we add a new class,
`java.lang.IO`, which declares five `static` methods:

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

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

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

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

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

Beginners do need to learn that the qualifier `IO` is required for these basic
line-oriented I/O methods, but that not an undue pedagogical burden. They are
likely to learn about such qualifiers soon anyway; e.g,. the qualifier `Math`
for mathematical functions such as `Math.sin(x)`.

Since the `IO` class resides in the `java.lang` package, it can be used without
an `import` in any Java program. This applies to all programs, not just those in
compact source files or those that declare instance `main` methods; for example:

```
class Hello {
    public static void main(String[] args) {
        String name = IO.readln("Please enter your name: ");
        IO.print("Pleased to meet you, ");
        IO.println(name);
    }
}
```

### 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 compact source file:

```
import java.util.List;

void main() {
    var authors = List.of("James", "Bill", "Guy", "Alex", "Dan", "Gavin");
    for (var name : authors) {
        IO.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 compact source files, as if they were imported on
demand. Popular classes and interfaces 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] proposes a new kind of import declaration, `import module M`,
which imports, on demand, all of the public top-level class and interfaces of
the packages exported by module&nbsp;`M`. Every compact 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 compact source file.


### Growing a program

A small program in a compact 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 compact 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 an import declaration. For example, this
compact source file:

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

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

```
import module java.base;

class NameLengths {
    void main() {
        var authors = List.of("James", "Bill", "Guy", "Alex", "Dan", "Gavin");
        for (var name : authors) {
            IO.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

### Automatically import the console I/O methods

In earlier previews of this feature, we explored the possibility of compact
source files automatically importing the `static` methods of the new `IO` class.
Thus developers could write `println(...)` in compact source files instead of
`IO.println(...)`.

This had the pleasing effect of making the methods in `IO` appear to be built-in
to the Java language, but it added a speed bump to the on-ramp: To evolve a
compact source file into an ordinary source file, a beginner would have to add a
`static` import declaration — another advanced concept. This runs contrary to
our second goal, namely that that beginners should be able to grow their code
gracefully. This design would also create a long-term burden of reviewing a
likely endless stream of proposals to add additional methods to the `IO` class.

### Automatically import fewer packages

Rather than automatically importing all 54 packages from the `java.base` module
into compact source files, we could instead import only some of them. But, which
ones?

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 compact source file. The list would, moreover, change as the Java Platform
evolves; e.g., `java.util.stream` and `java.util.function` were introduced only
in Java&nbsp;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 the classes implicitly declared by compact source
files.

### Allow top-level statements

An alternative design would allow statements to appear directly in a compact
source file, removing the need to declare a `main` method. This design would
interpret the entire compact source file as the body of an implicitly declared
`main` method of an implicitly declared class.

Unfortunately, this design would be limiting because it would be impossible to
declare methods in a compact source file. Such methods would be interpreted as
appearing in the body of an invisible `main` method, but that would make them
illegal since methods cannot be declared within methods. Compact source files
could only represent linear programs that consist of one statement after
another, without the power to abstract repeated computations into subroutines.

Furthermore, in this design, all variable declarations would be interpreted as
local variables of the invisible `main` method. This would be limiting because
local variables can only be accessed from lambda expressions when they are
effectively final, which is an advanced concept. Writing lambda expressions in
compact source files would be error-prone and confusing.

We believe that the desire to write statements directly in a compact source
file, outside of a method body, is largely driven by the pain of writing `public
static void main(String[] args)`. Having made `main` methods easier to declare,
we believe it best for compact source files to be composed of methods and fields
rather than statements.

### Extend JShell

[JShell] is an interactive tool for executing Java code immediately. It offers
an incremental environment for programming, allowing beginners to experiment
without a lot of ceremony.

An alternative design would extend JShell to achieve our goals. While an
attractive idea in theory, it is less appealing in practice.

A JShell session is not a Java program, but, rather, a series of _code
snippets_. Snippets are executed one at a time, but they are not independent:
The execution of the current snippet depends on the results of executing all of
the previous snippets, so values and declarations appear to evolve over time. At
any one moment, there is a notion of the current state of the program under
development, but there is no actual textual representation of the program. This
works well for experimentation — the primary use case for JShell — but it is not
a realistic foundation for helping beginners to write real programs.

At a more technical level, all declarations in a JShell session are interpreted
as `static` members of an unspecified class, and all statements are executed in
a context in which all previous declarations are in scope. If we were to
interpret a compact source file as a series of code snippets then the file could
only express classes whose methods and fields are `static`, in effect
introducing a Java dialect. Evolving a compact source file into an ordinary
source file would involve adding `static` modifiers to every method and field
declaration, hindering the graceful evolution of small programs to larger ones.

### Introduce a new dialect of the Java language

A radically different design would define a different dialect of the language
for use in compact 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 explicitly be 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.


[JEP&nbsp;445]: https://openjdk.org/jeps/445
[JEP&nbsp;463]: https://openjdk.org/jeps/463
[JEP&nbsp;477]: https://openjdk.org/jeps/477
[JEP&nbsp;495]: https://openjdk.org/jeps/495
[Hello,&nbsp;World!]: https://en.wikipedia.org/wiki/%22Hello,_World!%22_program
[§12.1]: https://docs.oracle.com/javase/specs/jls/se24/html/jls-12.html#jls-12.1
[§12.1.4]:https://docs.oracle.com/javase/specs/jls/se24/html/jls-12.html#jls-12.1.4
[companion JEP]: https://openjdk.org/jeps/511
[JShell]: https://openjdk.org/jeps/222