IntelliJ IDEA Java

‘HelloWorld’ and ‘main()’ meet minimalistic

“Just ignore the terms class, public, static and args[] for now; we’ll talk about it later”. If you have ever mentioned these lines to a new Java student, who is about to execute their first ‘HelloWorld’ program, know that this is changing. If you are that student, well, congratulations, getting started is simpler in Java :-).

Initially released as a preview language feature in Java 21, Implicitly Declared Classes and Instance Main Methods is currently being previewed for the second time in Java 22. This feature is for folks starting to learn Java. It simplifies the initial steps for students when they start learning basics, such as variable assignment, sequence, conditions and iteration. Students no longer need to declare an explicit class to develop their code, or write their main() method using this signature – public static void main(String []). With this feature, classes could be declared implicitly and the main() method can be created with a shorter list of keywords.

In this blog post, I’ll cover why and how this feature is helpful for students using hands-on coding examples. I’ll also cover some details and frequently asked questions relevant to educators or experienced programmers. Let’s get started.

Hi Students – Your HelloWorld code is changing


HelloWorld refers to the first piece of executable code that anyone writes when they start learning a new language or a framework. It usually includes executing bare minimum code, such as printing ‘Hello World’ on the console to ensure the setup is okay. In Java, it meant writing a class with a method main(). As the name suggests, main() is one of the most important methods. It defines the entry point of execution, when a class executes.

What happens when you see that your ‘HelloWorld’ code executed as expected? It brings a lot of joy and a sense of winning to the one executing it. I still remember the time when I was able to run my first piece of code in Java. I believe we kind of never forget our firsts, both in life, love and coding!

Let’s see how your HelloWorld code is changing.

Class ‘HelloWorld’ before and after Java 21

Before Java 21, you would need to define a class, say, HelloWorld, that defined a main() method with a specific list of keywords, to print any text, say, ‘Hello World’ to the console, as follows:

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

With Java 21, this initial step has been shortened. You can define a source code file, say, HelloWorld.java, with the following code, to print a message to the console (it doesn’t need to define a class; it has a shorter signature for method main()):

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

The preceding code is simpler than what was required earlier. Let’s see how this change could help you focus on what you need, rather than what you don’t.

Reducing cognitive complexity


One of the main benefits of the minimalistic main() method and not having to write an explicit class is – the code is concise and to the point. It introduces less concepts or keywords that could otherwise overwhelm new students.

Of course, you could ignore the terms class, public, static, args[] as suggested by your teacher, but they do use space in your memory every time you look at it. These little things pile up, in coding or life, overwhelming one. The minimalist main() and implicit enclosing class reduce cognitive complexity for you.

Compiling and executing your code


Once you are done writing your code, the next step is to execute it.

On the command prompt, you could use the javac and java commands to compile and execute your code. Assuming you have defined your code in a source code file HelloWorld.java, you could use the following commands to run and execute it:

C:\code\MyHelloWorldProject\javac HelloWorld.java
C:\code\MyHelloWorldProject\java HelloWorld

Since Java 11, it is possible to skip the compilation process for code defined in a single source code file, so you could use just the second command (by specifying the name of the source code file, as follows):

C:\code\MyHelloWorldProject\java HelloWorld.java

However, since instance main methods and implicit classes is a preview language feature, you should add the flag --enable-preview with --source 22 with these commands, as follows:

C:\code\MyHelloWorldProject\java --enable-preview --source 22 HelloWorld.java

Sooner or later, you might switch to using an IDE to write your code. If you wish to use IntelliJ IDEA for creating instance main methods, here’s a quick list of steps to follow. Create a new Java project, select the build system as IntelliJ (so you could use Java compiler and runtime tools), create a new file, say, HelloWorld.java with your instance main method and set the properties to use Java 22, before you run your code, as shown in the following gif (It could save you from typing out the compilation/ execution commands on the command prompt each time you want to execute your code):

Are you wondering if it would be better to create a ‘Java class’ instead of a ‘File’ in the ‘src’ folder? The option of selecting a Java class would generate the body of a bare minimum class, say, public class HelloWorld { }. Since we are trying to avoid unnecessary keywords in the beginning, I recommended creating a new ‘File’ which wouldn’t include any code.

If you plan to use IntelliJ IDEA, here are its configuration details. Java 22 support is available in IntelliJ IDEA 2024.1 EAP. The final release of this version is planned for March 2024.

In your Project Settings, set the SDK to Java 22. For the language level, select ‘22 (Preview) – Statements before super(), string templates (2nd preview etc.)’ on both the Project and Modules tab, as shown in the below settings screenshot:

Let’s move beyond the simple ‘Hello World’ code and explore further.

What else can main() do apart from printing messages to the console?


It is an interesting question. Let’s check out a few code examples below to learn more about it.

Example 1. Variable declarations, assignments and simple calculations


The initial steps in learning programming start with basics, such as declaring variables, assigning values, simple calculations and checking the values (by printing them to the console). Here’s a common example that covers them all – calculating the total amount returned by a bank for a specified interest rate and principal amount. Let’s define this code in a source code file, say, CalcInterest.java:

void main() {
    double principal = 17000;
    double rate = 0.07;
    double interest;

    interest = principal * rate;
    principal = principal + interest;

    System.out.println("Interest            = " + interest);
    System.out.println("Amount after 1 year = " + principal);
}

I know, the previous example seems like configuring your project-it is important but boring. Let’s talk about something more challenging but that could still be accomplished by a single main() method – like printing a pattern.

Example 2. Print patterns, such as, big letters using a specified character


Here’s another interesting problem that includes basics, such as, variable assignments, conditional statements (via if-else) and iteration (via for).

The following code prints a letter P, using the letter X (as specified by the variable charToPrint), that is 10 lines in height (as specified by the variable size):

void main() {
    int size = 10;
    char charToPrint = 'X';
    for (int i = 0; i < size; i++) {
        for (int j = 0; j < size; j++) {
            if (j = = 0 || (i = = 0 || i = = size / 2) && j < size - 1 || (j = = size - 1 && i <= size / 2)) {
                System.out.print(STR."\{charToPrint} ");
            } else {
                System.out.print("  ");
            }
        }
        System.out.println();
    }
}

The following gif shows the output of the preceding code:

Do you think you would be up for creating methods that could include logic to print other letters of the alphabet (A – Z), or even other alphabets (like the Devnagri)? Just asking :-)

Before we move forward, did you know you could overload methods, that is, define multiple methods with the same name, such as, ‘main’ in your source code file? Which of these methods main() method would execute when you run your source code file? IntelliJ IDEA displays the run icon next to the main() method in your source code. The following gif shows how that icon moves when you change the signature of a main method:

Let’s take a step forward in the next example by animating words spanning multiple lines.

Example 3. Animating multiline text – one word at a time


Soon, you’ll find yourself using other concepts like, say, iterating words in a String, single and multiple line comments, using other classes from core Java API like Threads, perhaps also some exception handling. Here’s an example of a multiline String that is displayed like animated text, one word at a time (all of which can be coded within the main() method):

/**
 * The main method animates the display of lines of text by printing each word with a pause in between.
 * It splits the given text into individual lines, then splits each line into words.
 * It prints each word followed by a space, and pauses for a specified amount of time between each word.
 */
void main() {
    // Text to animate
    String text = """
                  Why did the Java programmer bring a shovel to work? To 'dig' into the data structures!
                  Why was the Java developer always so calm? Because they never lost their stack trace!
                  Why did the Java developer go broke? Because he used up all his cache!
                  Why did the Java developer always carry a pen? To 'interface' with their notes!
                  """;

    // Split into individual lines
    String[] lines = text.split("\n");

    // Loop through lines
    for (String line : lines) {
        String[] words = line.split(" ");

        // Loop through words
        for (int i = 0; i < words.length; i++) {

	   // Print the word
            System.out.print(words[i] + " ");
            // Pause to create the animation effect
            try {
                Thread.sleep(200); // Change this number to modify word display speed
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        // Start printing on the next line
        System.out.println();
    }
}

The following gif shows the output of the preceding code:

If you are feeling adventurous, perhaps you could try other animations too – like animating each letter or decreasing the speed by which the words are printed to console. Are you up for this challenge :-)

Example 4. Data structure problems


I’ve seen that both students and experienced programmers enjoying solving problems that involve data structures. So, here’s one for you (which can be coded using just the main() method): Given an array of integers, find the maximum subarray sum. The subarray must be contiguous, but could be of any length.

Below is the code for your reference:

void main(String[] args) {
    int[] nums = {-2, 1, -3, 4, -1, 2, 1, -5, 4};
    int maxEndingHere = nums[0];
    int maxSoFar = nums[0];

    for (int i = 1; i < nums.length; i++) {
        maxEndingHere = Math.max(nums[i], maxEndingHere + nums[i]);
        maxSoFar = Math.max(maxSoFar, maxEndingHere);
    }

    int maxSum = maxSoFar;
    System.out.println("Maximum subarray sum: " + maxSum);
}

Example 5. Text based Hangman game


Here’s another example that shows code for a text based hangman game – a simple game that asks you to guess one letter at a time. If the letter appears in the target guess word, it reveals itself in the word. This example uses the Scanner class from the Java API, which could be accessed either by using the full name while defining its variable, or by including an import statement outside the main() method (this helps you to get familiar with using concepts like importing other classes in an incremental way):

import java.util.Scanner;

void main() {
    Scanner scanner = new Scanner(System.in);

    String[] words = {"java", "programming", "computer", "algorithm", "developer"};
    String targetWord = words[(int) (Math.random() * words.length)];

    StringBuilder guessedLetters = new StringBuilder(targetWord.length());
    for (int i = 0; i < targetWord.length(); i++) {
        guessedLetters.append("_");
    }

    int attempts = 6;
    System.out.println("Welcome to Hangman!");
    System.out.println("Try to guess the word. You have " + attempts + " attempts.");

    while (attempts > 0) {
        System.out.println("Current word: " + guessedLetters);
        System.out.print("Enter a letter: ");
        char guess = scanner.next().charAt(0);

        boolean found = false;
        StringBuilder updatedWord = new StringBuilder(guessedLetters);
        for (int i = 0; i < targetWord.length(); i++) {
            if (targetWord.charAt(i) == guess) {
                updatedWord.setCharAt(i, guess);
                found = true;
            }
        }

        if (found) {
            guessedLetters = updatedWord;
            System.out.println("Correct guess! Updated word: " + guessedLetters);
        } else {
            attempts--;
            System.out.println("Incorrect guess! You have " + attempts + " attempts left.");
        }

        if (guessedLetters.toString().equals(targetWord)) {
            System.out.println("Congratulations! You guessed the word: " + targetWord);
            break;
        }
    }

    if (attempts == 0) {
        System.out.println("Game Over. The word was: " + targetWord);
    }

    scanner.close();
}

The following gif shows the output of the preceding code:

Here’s another challenge for you – I just realized that the input in the preceding code is case sensitive. It doesn’t work if you type ‘A’ instead of ‘a’. Could you make it ignore the case of the letters that are typed?

The intent with all these examples is that you could use minimum ceremonial code to start learning basics in Java.

Changing an implicit class to a regular class


When you are ready to level up and work with other concepts like user defined classes, you could also covert the implicit classes and code that we used in the previous examples, to regular classes, as shown below:

In the next section, I’ll answer a few questions that might seem more interesting for folks who are not new to Java and are interested in knowing more about this feature.

Some details and FAQs


Let’s start by sharing that the name of this feature changed from its initial release, which is not common.

A little bit of history

The feature implicitly declared classes and instance main method was initially previewed in Java 21, with a slightly different name, that is, Unnamed Classes and Instance Main Methods. It is not usual for Java features to change their name across their preview versions. However, in this case the name was changed because there have been changes to it in its second review.

In its second preview, when a main method is defined without an enclosing class, it is implicitly declared as a final class during the compilation process.

What do you mean by ‘instance’ main() method?


This feature refers to the main method as an instance main method since it doesn’t use the keywords static with its declaration. In the past, the main method was defined using the static keyword, so that it could be accessed even without the need of creating any objects of the class in which it was defined.

Behind the scenes, a source code file with an instance main method is compiled to a regular class with a no-arg constructor. Its instance main method is executed by creating an instance of this class, followed by calling main() on it.

What happens when you create a source code file with method main(), but no class declaration?


Behind the scenes, the Java compiler creates an implicit top level class, with a no-argument constructor, so that these classes don’t need to be treated in a way that is different to the regular classes.

Here’s a gif that shows a decompiled class for you for the source code file AnimateText.java:

Does this feature introduce a separate beginner’s toolchain?


I’m an educator and I fear that if we introduce simpler processes to students that are not mainstream, that could impact how they transition to learning other Java concepts later. I’m glad to share that this feature doesn’t do that.

With implicit classes and instance main methods, students can still compile and run their code using the same tools and process, as are required when the size of their sample code or count for their source code files increase.

Variations of the main method in the implicit class


As we are aware, a method can be overloaded. Does that imply an implicit class can define multiple main methods? If yes, which one of them qualifies as the ‘main’ method?
This is an interesting question. First of all, know that you can’t define a static and non-static main method with the same signature, that is, with the same method parameters. The following method are considered valid main() methods in an implicit class:

public static void main(String args[]) {}
public void main(String args[]) {}
public static void main() {}
static void main() {}
public void main() {}
void main() {}

If there is no valid main method detected, IntelliJ IDEA could add one for you, as shown in the following gif:

Educators could use this feature to introduce other concepts to the students in an incremental way


If you are an educator, you could introduce your students to other commonly used programming practices, such as creating methods- that is delegating part of your code to another method and calling it from the main method. You could also talk about passing values vs. variables to these methods.

The following gif shows how to do it:

There are other practices too that I would have loved to mention like implementing the abstract methods of an interface, but, I think it would be better to leave them for another blog post.

Preview Features


Implicitly Declared Classes and Instance Main Methods is in its second preview in Java 22. With Java’s new release cadence of six months, new language features are released as preview features. They may be reintroduced in later Java versions in the second or more preview, with or without changes. Once they are stable enough, they may be added to Java as a standard language feature.

Preview language features are complete but not permanent, which essentially means that these features are ready to be used by developers, although their finer details could change in future Java releases depending on developer feedback. Unlike an API, language features can’t be deprecated in the future. So, if you have feedback about any of the preview language features, feel free to share it on the JDK mailing list (free registration required).

Because of how these features work, IntelliJ IDEA is committed to only supporting preview features for the current JDK. Preview language features can change across Java versions, until they are dropped or added as a standard language feature. Code that uses a preview language feature from an older release of the Java SE Platform might not compile or run on a newer release.

Summary


Java language designers are reducing the ceremony that is required to write the first HelloWorld code for Java students, by introducing implicitly declared classes and instance main methods. New students can start with bare minimum main() method, such as, void main() and build strong programming foundation by polishing their skills with basics like sequence, selection and iteration.

Some folks might argue that this is a simple change, or that other languages already have it. For the first point, I’d say, it is better to be late than never. Also, in life and coding, it is always better to focus on what you want to do, rather than focusing on what you don’t want to. For the latter point, I’d say, good for those languages :-)

image description