Looking at Java 21: Simpler Main Methods and Unnamed Classes

 ยท 6 min
Braden Collum on Unsplash

Java’s known (and often criticized) for its vast amount of required boilerplate and ceremony for perceived mundane tasks, at least compared to other languages. Despite the “war on boilerplate” not being the primary goal of most new features, it’s often a side effect of new additions to the language, like Records. The preview JEP 445, however, is explicitly targeted to make the first introduction to Java a breeze: simpler main methods and unnamed classes.


Simpler Main Methods

Even though Java is an excellent language for complex, large, enterprise-grade multi-developer applications, it’s still intended to be a first programming language, too. That’s why the initial hurdles should be as low as possible.

Let’s take a look at how to write your first Java program.

How to Start a (Java) Program

We all know how a typical Java program starts:

java
package com.beliefdrivendesign;

public class MyAwesomeApp {

  public static void main(String... args) {
    // Awesome code goes here
  }
}

It’s the “normal” entry point all of us have written many times, and in complex applications, we do it once and don’t think much about it anymore. But imagine yourself being a beginner again and wanting to learn Java. You don’t know much about Java yet and are confronted with a lot of things right now…

A package declaration, which might be there, maybe not.

The class declaration uses a mandatory visibility modifier (public) that doesn’t make sense for our first program. And if you name the file containing it not the same as the class, it won’t compile.

Next, there’s a method aptly named main, so it’s the actual entry point. However, it uses a visibility modifier (public), too, but adds in a lifetime/access modifier (static) for good measure. To make it even more complicated, let’s introduce var-args (String...) or an array (String[]) right in your first program.

I’ll use var-args throughout the article, but it’s interchangeable with an array.

Most of these features you need to learn at some point, but they’re primarily targeted at more complex applications. Without knowing the details of these features, you might think that static methods are the way to go and form a bad habit from the start.

Compare all that to the requirements for your first Python program:

python
# Awesome code goes here

That’s even the code for your first Ruby program!

So I think that perfectly illustrates what people mean when they talk about ceremony and boilerplate. For a seasoned developer and in the context of a complex program, it might make little difference, and each of the features is necessary and make sense. For newcomers, though, it’s an unnecessary hurdle compared to many other languages.

Less is More

Let’s review the Java example again and remove all the “unnecessary” things.

The package declaration isn’t necessary even before Java 21, so it’s omitted. But how about removing visibility/lifetime modifiers and the method arguments?

java
class MyAwesomeApp {

  void main() {
    // Awesome code goes here
  }
}

Still not as sleek like in other languages, but already a great improvement! And that’s exactly what the flexible launch protocol of JEP 445 will introduce:

  • No visibility modifier is needed for the class declaration
  • main method lookup mechanic, searching for the first available entry point of:
    • Any non-private static void main(String... args) method
    • Any non-private static void main() method
    • A void main(String...) method
    • A void main() method

By allowing us to remove much of the ceremony for single-class programs, the code isn’t as intimidating as before, and we even have a choice of options!

The best thing, though, is that it’s implemented in a Java-compatible way. There’s no special syntax needed, it’s all valid code.

Removing modifiers and method arguments isn’t all that JEP 445 contains, though.


Unnamed Classes

The way Java projects are structured is simple: every class is in a package and every package is in a module. This method of namespacing and compartmentalizing your code is prevalent in many programming languages and a necessity for any program consisting of more than a few classes. However, just like public static void main(String... args) method in the previous section, it’s another hurdle for simple, one-class programs.

That’s why JEP 445 also introduces unnamed classes, simplifying our code even further.

java
void main() {
  // Awesome code goes here
}

The change that makes this possible is how the compiler treats a source with methods and fields not enclosed in a class. Any unenclosed fields and methods, and nested classes, declared in the file build an unnamed top-level class.

As the class has no name, we can’t reference or instantiate it any way ourselves. It resides in the unnamed package in the unnamed module. Besides that, it behaves almost like a “normal” class declaration. We can’t use certain features, though, like implementing an interface, extending another type, referencing the class by name, generating documentation with javadoc, etc.

Even though the code is no longer valid Java syntax on the surface, it still is behind the scenes. If the code doesn’t have a static main method, running an unnamed class is equivalent to:

java
new Object() {

  void main() {
    // Awesome code goes here
  }

}.main();

Why This Matters

As a seasoned developer with years of Java under your belt you might not see why this is important, especially since you had to endure it and still become an awesome Java dev. Still, Java throws a lot at newcomers that can be avoided, as this JEP shows.

Newcomers should only need to concentrate on the relevant code for their first contact, so omitting concepts and constructs they don’t immediately need makes a lot of sense. It’s not that they don’t need to learn them at some point, but now they can choose to do so at their own pace.

Ok, so why not make it even simpler by removing the main method altogether? Well, because the Java language designers put quite a bit of thought into any new addition, especially if it affects the language itself. As I talked about in the previous article, Java’s language design strives towards improvement without compromise.

Removing the main method and treating the whole code as an executable logic unit would affect other Java semantics, such as how local variables behave differently from fields, for example. That might not be a big deal in many cases but would hinder growing your unnamed class into a bigger program, as you progress in your learning journey.

Should I use a Preview Feature?

As both features, simpler main methods and unnamed classes, are only relevant to single-class programs, I don’t see any harm in using them.

You can use the preview feature in two ways:

Compiling and running your code

shell
# COMPILE
javac --release 21 --enable-preview MyAwesomeApp.java

# RUN
java --enable-preview MyAwesomeApp

Using the Source Code Launcher (JEP 330)

shell
java --source 21 --enable-preview MyAwesomeApp.java

A Functional Approach to Java Cover Image
Interested in using functional concepts and techniques in your Java code?
Check out my book!
Available in English, Polish, and soon, Chinese.

Resources

Looking at Java 21