Will Java 22 Kill Compiler Tools? / Hebrew

Will Java 22 Kill Compiler Tools? / Hebrew

Introduction

I am Mykola Parlog, a representative of the Java development department at Oracle, and I will answer the question in the title – no, of course not! How could you even think of such a thing!

But it does reduce their relevance a bit, and in the right circumstances, that’s a very good thing. I’ll explain what I mean later. First, let’s take a look at how this new feature works. Are you ready? Then let’s dive head first!

The Java 22 version has been enriched with the ability to execute source code from multiple files. In this case, a simple java command is enough to execute programs that consist of several source files and even contain dependencies. For experienced developers, this will simplify the search and experiments, but for those who are just learning Java or just programming, this is a real revolution: now you can write Java code from one or more source files, and even add dependencies regardless of the IDE or assembly tools.

Look:

project-folder
 └─ Hello.java

$ java Hello.java

So we have one Java code file and I’m going to execute it using the java command.

java Hello.java

It just runs without pre-compiling. But this is no longer news. Well, let’s try something new:

project-folder
 ├─ Hello.java
 └─ Greetings.java

$ java Hello.java

Do you see this file? Greeting.java? Let’s run it. I will execute the same command…Bang! Still working. Even the result has changed.

But that’s not all. See the JAR archive in the lib folder? Wait, let me open it. See this JAR?

project-folder
 ├─ lib
 │   └─ audience.jar
 ├─ Hello.java
 └─ Greetings.java

$ java -cp "lib/*" Hello.java

Let’s try this too. Now I need to change the command a bit… here it goes: Everything works!

That’s it: Java 22 can run multiple source files and even their JAR dependencies without requiring a javac call, let alone a jar. Is it possible to hide Maven and Gradle?

Running multiple source code files

You already get the point. In Java 11 and above, it is possible to compile in memory and then run a single source file using only one java launcher, telling it the path to the source file. Version 22 introduced the following change: if this file references classes from other files, Java will look for them and, if successful, continue compilation and execution.

Let’s see how Java finds these files. In the example I showed you, the source file (the one I passed as the launcher argument) does not contain a package declaration and is therefore in an unnamed package. In this case, Java considers the folder containing it to be the root of the source tree and will allow all classes from there:

  • This means that other classes in the unnamed package will also be in this root directory.
  • For classes in named packages, the package will map to the folder hierarchy in the usual way, where each section of the package name corresponds to a folder. This hierarchy will then be bound to the root of the package, and the class will be found in the output path.
project-folder # ③ source tree root
 ├─ org # ⑦ searched in folder hierarchy …
 │   └─ example # … that matches pkg name …
 │       └─ Audience.java # … anchored in root
 ├─ Greeting.java # ⑤ searched in root
 └─ Hello.java # ① initial source file

$ java Hello.java # ① initial source file
// Hello.java
// ② объявление пакета отсутствует ⇝ безымянный пакет
class Hello {

	public static void main(String[] args) {
		System.out.println(
			Greeting.get() // ④ в безымянном пакете
				+ ", "
				+ org.example.Audience.get()");
				// ↑↑↑ ⑥ в именованном пакете
	}

}

If the classes cannot be found in these locations, we will get an error. Quite simple and logical, as it seems to me.

If a package is declared in the source file, the file must be in the folder hierarchy that matches the package name as just described. Determining the root of the source tree is a bit more complicated, but I’ll spare you the details because here we’re just discussing implementations of the same idea: The root is the folder containing the directory hierarchy corresponding to the package name.

project-folder # ③ корень дерева исходников, основанного на …
 └─ org         # … walking up org/example/ …
     └─ example # … (matches the package name) …
         ├─ Audience.java
         ├─ Greeting.java
         └─ Hello.java # … from initial src

$ java org/example/Hello.java # ① initial src
// Hello.java
// ② package name
package org.example;
class Hello { /* ... */ }

The great thing about Java is that it doesn’t just interpret the working directory as the root of the source tree, it doesn’t matter where you run the program from and what path you have to specify to get to the main class. If the project structure is OK, the program will be launched.

project-folder
 └─ org
     └─ example
         ├─ Audience.java
         ├─ Greeting.java
         └─ Hello.java

# Все эти команды работают из

# из каталога home/nipa/code/project-folder:
$ java org/example/Hello.java

# из home/nipa/code:
$ java project-folder/org/example/Hello.java

# из home/nipa/code/project-folder/org/example:
$ java Hello.java

Running with dependencies

Adding dependencies is easy. Just use the –class-path option or its short form -cp and point to the folder containing the JAR files.

No, wait, that won’t actually work because class path doesn’t understand what a folder is. You’ll have to point to the folder with $folderName/*, and at least on Linux you’ll need to enclose this code in quotes to avoid shell expansion. So, if your JAR files are in the lib folder, add -cp “lib/*” to the java command; or \ for Windozers.

project-folder
 ├─ lib
 │   └─ audience.jar
 ├─ Hello.java
 └─ Greetings.java

$ java -cp "lib/*" Hello.java
// Hello.java
class Hello {

	public static void main(String[] args) {
		System.out.println(Greeting.get() + ", " + org.example.Audience.get());
	}

}

// Greeting.java
class Greeting {

	static String get() {
		return "Hello";
	}

}

Some details about compilation

A few more aspects must be taken into account when compiling:

  1. Java only compiles files that are directly or indirectly referenced by the source file, which means you can have source files with compilation errors lying in the same folder hierarchy if you don’t reference them and the program will still work .
  2. The order in which the various files are compiled is not guaranteed, and will generally happen before or after the start of the main function.
  3. This “dynamic compilation” means that it is possible to get compilation errors while the program is running, which is not what we are used to.

And a few more details, but I’ll leave them up to you. One interesting aspect hidden in these details is the design philosophy. It’s not just “make things easy”, it’s also “make smooth transitions from single source to multiple and from multiple to JAR”. These steps should be natural and require little or no adaptation to the new situation.

But why?

Now that we have a better understanding of the mechanics of running multiple source files, let’s discuss why it was introduced. This task was solved

JDK Enhancement Proposal 458

. IN

JDK 22

this feature is in the final version, not as a preview.

But let’s get back to why this is needed. After all, part of the tasks that are already solved at the level of the IDE and assembly tools are implemented here, so what’s the point? For experienced developers, this functionality is not needed in situations where it is not difficult to quickly set up a new project. Just keep doing what you’re doing.

But maybe you’re experimenting with a new feature, participating in an Advent of Code contest, wanting to find the fastest way to steam 1 billion lines, or exploring an unknown task space in hopes of assembling a working prototype. Then a light editor and a few 2D files might do you good, at least for a while. And with the described innovation, it is possible to achieve much more than executing a single source file before having to study in depth what project structure is compatible with the existing build tool. But in this case, you will need to manage dependencies or collect artifacts.

Let an experienced developer find it inconvenient to be distracted by structuring the project and setting up tools (the rhythm of the work goes astray), this is just an unpleasant little thing. But imagine you’re starting out, trying to understand basic programming concepts, the Java language, some APIs. Once you’ve practiced enough and are ready to write a program larger than one file, it’s worth taking a break and understanding what build tools or IDEs are, how they work, which ones to use, how to configure them, etc. which it is desirable to get rid of.

I still remember how shocked and amazed I was when I first saw Eclipse and POM. And, by the way, it’s about me! The correct construction of the project is associated with difficulties. And that’s why it’s great that this add-on helps postpone that complexity until you actually want to build a project, rather than just experimenting with code.

Related posts