Unnamed Classes and Instance Main: JEP 445/463 (Java 21+)
Traditional Java requires every program to live in a named class with a public static void main(String[] args) method: a significant hurdle for beginners and script authors. JEP 445 (preview in Java 21) and its follow-up JEP 463 (preview in Java 22) relax these constraints: you can write an instance main method, drop the String[] parameter, and, at the extreme, write a top-level method in a file with no class declaration at all. An "unnamed class".
Contents
Instance Main Method
Before JEP 445/463 the only valid entry point was public static void main(String[] args)
: non-public, non-static (instance method), with argsvoid main(String[] args) : instance method, no args at allvoid main()- The classic
still works (highest priority)public static void main(String[] args)
// Classic — still valid
public class Hello {
public static void main(String[] args) {
System.out.println("Hello from classic main");
}
}
// Instance main — no static, no public required
class Hello {
void main() {
System.out.println("Hello from instance main");
}
}
// Instance main with args — still no static needed
class Hello {
void main(String[] args) {
System.out.println("Args: " + java.util.Arrays.toString(args));
}
}
mainmainUnnamed Classes
An unnamed class is a source file where top-level methods and fields are declared without an enclosing class declaration. The compiler implicitly wraps the contents in a class whose name is derived from the file name.
// File: Hello.java (no class declaration needed!)
void main() {
System.out.println("Hello, World!");
}
// File: Greeter.java
// Top-level fields and helper methods are allowed
String greeting = "Hello"; // instance field
void main() {
greet("World");
greet("Java");
}
void greet(String name) {
System.out.println(greeting + ", " + name + "!");
}
// File: FibScript.java
// Fibonacci — no class boilerplate
int fib(int n) {
return n <= 1 ? n : fib(n - 1) + fib(n - 2);
}
void main() {
for (int i = 0; i <= 10; i++) {
System.out.print(fib(i) + " ");
}
// 0 1 1 2 3 5 8 13 21 34 55
}
The implicit class has package-private
Compilation: --enable-preview
As of Java 21–23 these features are in preview and require the --enable-preview
# Compile with preview (Java 21)
javac --enable-preview --release 21 Hello.java
# Run with preview
java --enable-preview Hello
# Or use the single-file source launcher (Java 11+)
java --enable-preview --source 21 Hello.java
# Java 22 — JEP 463 (second preview)
javac --enable-preview --release 22 FibScript.java
java --enable-preview FibScript
Limitations vs Named Classes
- No explicit class declaration: unnamed classes cannot be declared
,public , orabstract .final - Cannot be extended or referenced: other source files cannot import or reference an unnamed class by name.
- No package declaration: unnamed classes always belong to the unnamed package.
- No constructors with parameters: the launcher uses the no-arg constructor; you cannot define parameterized constructors.
- Cannot implement interfaces or extend classes explicitly: there is no syntax to add
orimplements when there is no class header.extends - Static members are limited: you can declare static methods and static fields at the top level, but they belong to the implicit class.
// This is NOT possible in an unnamed class:
// public class Script { ... } — no class header
// class Script implements Runnable — no implements
// package com.example; — no package
// Static helper is fine:
static int square(int n) { return n * n; }
void main() {
System.out.println(square(7)); // 49
}
Use Cases: Scripts and Learning
Unnamed classes and instance main target two primary audiences: beginners learning Java for the first time, and script authors writing quick utilities.
// File: SortDemo.java — a concise teaching example
import java.util.*;
var numbers = List.of(5, 2, 8, 1, 9, 3);
void main() {
var sorted = numbers.stream().sorted().toList();
System.out.println("Unsorted: " + numbers);
System.out.println("Sorted: " + sorted);
System.out.println("Max: " + Collections.max(numbers));
}
// File: HttpPing.java — quick script using HttpClient
import java.net.http.*;
import java.net.URI;
var client = HttpClient.newHttpClient();
void main() throws Exception {
var request = HttpRequest.newBuilder()
.uri(URI.create("https://example.com"))
.GET()
.build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println("Status: " + response.statusCode());
System.out.println(response.body().substring(0, 100));
}
// File: WordCount.java — process files from command line
import java.nio.file.*;
void main(String[] args) throws Exception {
if (args.length == 0) {
System.err.println("Usage: java WordCount.java <file>");
return;
}
long count = Files.lines(Path.of(args[0]))
.flatMap(line -> java.util.Arrays.stream(line.split("\\s+")))
.filter(w -> !w.isBlank())
.count();
System.out.println("Words in " + args[0] + ": " + count);
}