Java 14: Record, a more concise instanceof, jpackage wrapper, switch lambdas and text blocks

UPD. Today will be the long-awaited release of Java 14 - and even if it is not LTS - there are enough new features in it. Java 14 will be available in a few hours - but you can get to know it now.



In Java 14, there are enough changes, both at the level of writing code, and at the level of API, GC and many other engine hoods. We can say with some certainty that if you know about some Kotlin or Python super-chips - do not worry, with a high degree of probability they will soon appear in Java. In any case, today's release contains some of them. But first things first.

In Java 14, the following innovations await us:

  • JEP 305. Assigning a reference to an object being checked through instanceof.
  • JEP 343. The packer jpackage (Incubator).
  • JEP 345. NUMA-based memory allocation for G1.
  • JEP 349. Streaming events through the JFR.
  • JEP 352. Immutable Mapped Byte Buffers.
  • JEP 358. Tips on NullPointerException.
  • Jep 359. Record.
  • JEP 361. Switch lambdas.
  • JEP 362. The Solaris and SPARC ports are now Deprecated.
  • JEP 363. Removing the Concurent Mark Sweep garbage collector that was previously marked as Deprecated.
  • JEP 364. Porting ZGC on macOS.
  • JEP 365. Porting ZGC to Windows.
  • JEP 366. The combination of ParallelScavenge + SerialOld GC is now Deprecated.
  • JEP 367. Removing tools and APIs from Pack200 (which were marked as Deprecated back in Java 11).
  • JEP 368. Text Blocks.
  • JEP 370. External Memory Access API (Incubator).

Let's talk about each improvement, some of which will be discussed in more detail.

JEP 305. Assigning a reference to an object checked through instanceof




Consider the situation of checking the type of an object through instanceof. If we want to explicitly customize an object to the required type without risking a ClassCastException, we first need to make sure that the object is the type we need.

    private static void showNameIfToy(Object o) {
        if (o instanceof Toy) {  //,    -  Toy
            Toy t = (Toy) o;  //  
            System.out.println("Toy's name: " + t.getName());  //-   
        }
    }

In this example, which occurs, by the way, all the time, we a) make sure that we have an object of the desired type, b) assign a link to it, c) do something with it, based on our logic. At Oracle, it seems, they saw a certain splendor of code in this particular construction, and decided to reduce it by exactly one step. Now you can write like this:

    private static void showNameIfToy(Object o) {
        if (o instanceof Toy t) {  //      
            System.out.println("Toy's name: " + t.getName());  //     
        }
    }

How all this is really convenient and useful, I leave to judge you, reader. Yes, indeed, this construction in this form occurs, probably, in 99% of cases associated with instanceof, and, perhaps, simplification will take root (or maybe not). Time will tell.

JEP 343. The jpackage Packer (Incubator)




We, the developers, are guys who are shot, you won’t scare us with installing a dzharnik, but what about a simple user who does not want to know when installing the JVM that it is installed on another 3 billion machines, does not want to know about Java cross-platform, but just wants to poke 2 times the .exe file if it has Windows, or just drag the .app application to the Applications folder, if it has a poppy, and don’t steam it? How to create all conditions for programmers so that they pack their applications in “executables” familiar to the end user?

It seems that Oracle realized the problem and decided to write a packer that will immediately package applications in a format suitable for the platform:

  • Linux: deb and rpm
  • macOS: pkg and dmg
  • Windows: msi and exe

It looks something like this:

$ jpackage --name myapp --input lib --main-jar main.jar --type exe

--name myapp - future name of the application
--input lib - source of the jar archive
--main-jar main.jar - name of the archive containing the main class
--type exe - type into which the application will be packaged.

After packaging, you can double-click on myapp.exe (if you have Windows) and install it as a regular windows application. The graphical interface is provided by JavaFX.

Let's try to build such an application using the Windows platform.

So, let's take the project from here:
https://github.com/promoscow/bouncer
When sending a GET request, we get the message: “Bounce successfull” and timestamp.

Putting a dzharnik. Since we have gradle, it is in the build / libs folder.



Go to the build folder, enter the required minimum of commands:

jpackage --name bouncer --input libs --main-jar bouncer.jar --type exe

We get, really, an exe-shnik.



We poke twice in the executable, the usual installation of the application occurs.
Wow!



The application has been installed and is waiting in the wings.
In Program Files, in the bouncer folder, you can run bouncer.exe.



JEP 345. NUMA-allocated memory allocation for G1


There is such a problem - NUMA, Non-Uniform Memory Access, Unequal memory access. In other words, access to remote sockets in multi-section machines can take considerable time, especially for the G1 garbage collector. In Java 14, they tried to solve this problem as follows: The

G1 heap is organized as a set of fixed-size areas. A region is usually a collection of physical pages, although when using large pages (via -XX: + UseLargePages), several regions can make up one physical page.

If you add the + XX: + UseNUMA parameter during JVM initialization, the regions will be evenly distributed over the total number of available NUMA nodes.

Oracle promises that although this approach is not entirely flexible, they will improve it in the future.

JEP 349. Streaming Events Through the JFR


There is such a thing in Java as JFR - Java Flight Recording - recording events on the fly, which allows you to monitor about 500 varieties of events while the application is running. The problem is that most of them can only be viewed in the logs. The improvement offered by Oracle is to implement a handler, for example, a lambda function that will be called in response to an event.

Here's what it looks like:

try (var rs = new RecordingStream()) {
  rs.enable("jdk.CPULoad").withPeriod(Duration.ofSeconds(1));
  rs.onEvent("jdk.CPULoad", event -> System.out.println(event.getFloat("machineTotal")));
  rs.start();
}

This event every second displays the processor load to the console.



JEP 358. Tips on NullPointerException


Another obvious convenience, designed to simplify the search for errors in the code. Imagine a construction - a planet, there are many countries on the planet, in every country there are many cities.

public class Planet {

    private List<Country> countries;
    //...
}

public class Country {

    private List<City> cities;
    //...
}

public class City {

    private String name;
    //...
}

We decided to display the hash codes of all the cities on the planet:

planet.getCountries().forEach(c -> c.getCities().forEach(city -> city.hashCode()));

But they did not think about the mandatory initialization of the fields. And at some point they got a NullPointerException:

Exception in thread "main" java.lang.NullPointerException
	at ru.xpendence.jep_358_nullpointerexception.Main.main(Main.java:19)

Which field do we have null? planet? country? city ​​??? We do not know. We put the breakpoint on the right line and, with a sigh, go debase.

In Java 14, a NullPointerException is more informative:

Exception in thread "main" java.lang.NullPointerException: Cannot assign field "cities" because "countries" is null
     at Main.main(Main.java:21)
     ...

And immediately everything is clear. countries is null.

JEP 359. Record




No wonder Oracle changed the release cycle to a six-month one. Tectonic shifts in the IT industry make even such leaders move faster. And if, for example, Kotlin and Python at the time of their appearance were influenced by Java (this is what Wikipedia says about Python, anyway), now Java is looking at its followers, so to speak. About Python it will be lower, but the following feature of Oracle was precisely looked in Kotlin. It's about data classes, which in Java are now called record.

What is a data class? Let's write a regular POJO:

public class Station {

    private String name;
    private Coordinates coordinates;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Coordinates getCoordinates() {
        return coordinates;
    }

    public void setCoordinates(Coordinates coordinates) {
        this.coordinates = coordinates;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        PlainStation that = (PlainStation) o;
        return Objects.equals(name, that.name) &&
                Objects.equals(coordinates, that.coordinates);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, coordinates);
    }

    @Override
    public String toString() {
        return "PlainStation{" +
                "name='" + name + '\'' +
                ", coordinates=" + coordinates +
                '}';
    }
}

Here we have everything - getters, setters, equals, hashcode, toString ... In order to somehow get rid of this disgrace, good people came up with Lombok.

At Jetbrains, the problem was solved at one time in a more radical way - by inventing data classes for Kotlin. Such a class with all standard methods looks like this:

data class Station(val name: String, val coordinates: Coordinates)

And that’s it. In Oracle, it seems, looked at this design and did exactly the same, only record:

public record RecordStation(String name, List<Coordinates> coordinates) {}

It contains standard getters, setters, equals, hashcode and toString.
A class of this type can already be created in IDEA 2020.1.

What are the differences from POJO?


  • Record cannot be inherited from any class.
  • Record cannot have any other fields of the object, except for those declared in the constructor when describing the class (yes, this is the default constructor, by the way). Static - you can.
  • Fields are implicitly final. Objects are implicitly final. With all the consequences, like the impossibility of being abstract.

It turns out that this is such an immutable data-class, not intended for some complex logical actions with it, but intended for data transfer, and that’s all.

JEP 361. Switch Lambdas




It seems that Oracle has taken up the switch tightly. Prior to the current release, it was a rather bulky design, and it looked like this:

    public static void translateDayOfWeekOld(String dayOfWeek) {
        switch (dayOfWeek) {
            case "MONDAY":
                System.out.println("");
                break;
            case "TUESDAY":
                System.out.println("");
                break;
            case "WEDNESDAY":
                System.out.println("");
                break;
            case "THURSDAY":
                System.out.println("");
                break;
            case "FRIDAY":
                System.out.println("");
                break;
            case "SATURDAY":
                System.out.println("");
                break;
            case "SUNDAY":
                System.out.println("");
                break;
            default:
                System.out.println("Day of week not found, try again with today day of week");
                String displayName = LocalDate.now().getDayOfWeek().name();
                translateDayOfWeek(displayName);
        }
    }

Processing one condition takes at least three lines - case, action, break. Now, with the enhanced functionality of the switch, we can shorten the above construction to this:

    public static void translateDayOfWeek(String dayOfWeek) {
        switch (dayOfWeek) {
            case "MONDAY" -> System.out.println("");
            case "TUESDAY" -> System.out.println("");
            case "WEDNESDAY" -> System.out.println("");
            case "THURSDAY" -> System.out.println("");
            case "FRIDAY" -> System.out.println("");
            case "SATURDAY" -> System.out.println("");
            case "SUNDAY" -> System.out.println("");
            default -> {
                System.out.println("Day of week not found, try again with today day of week");
                String displayName = LocalDate.now().getDayOfWeek().name();
                translateDayOfWeek(displayName);
            }
        }
    }

Agree, quite compact, fashionable and with lambdas. Is it worth it, you decide.

By the way, switch IDEA 2020.1, written according to the old rules, carefully suggests rewriting in a new way.



JEP 362. Solaris and SPARC Ports Now Deprecated


Everything is simple here. Oracle decided that it was not worth spending resources on supporting Solaris and SPARC ports, and freed employees should switch to the development of new features.

JEP 363. Removing the Concurent Mark Sweep garbage collector that was previously marked as Deprecated


The removal of the CMS garbage collector was discussed two years ago, in the 9th release. During this time, two garbage collectors came out - ZGC and Shenandoah. At the same time, not one of the trustworthy Oracle contributors paid attention to CMS support.

In general, the doctor said to the morgue - then to the morgue.

JEP 364, 365. Porting ZGC on macOS and Windows


We typically deploy applications on Linux platforms, but for local development we often use Windows and Mac. For these needs, Java 14 ported the ZGC garbage collector to these two platforms.

JEP 366. ParallelScavenge + SerialOld GC Combination Now Deprecated


There is an extremely rarely used garbage collector configuration - when the young ParallelScavenge is combined with SerialOld. Why this is done is not clear, since ParallelScavenge is parallel, and SerialOld, on the contrary, is not. The inclusion of this combination requires special dances with a tambourine and requires a lot of blood developers. “Oracle cares about you,” so it marks this configuration as obsolete and hopes to send it soon to the CMS garbage collector.

Let us rejoice, brothers.

JEP 367. Removing Pack200 tools and APIs (which were marked as Deprecated back in Java 11)


The turn of pack200, unpack200 and API pack200 came for a well-deserved rest. And the reason is the obsolescence of this packer. Once upon a time, when the Internet was modem and its speed was 56k (boomers remember), JDK had to be pumped out for hours. A packer was invented that would compress JDK better and reduce download time. Also, they could compress applets and client applications. But time passed, and at current speeds, the packer is not relevant.

The following packages will be removed:

java.util.jar.Pack200
java.util.jar.Pack200.Packer
java.util.jar.Pack200.Unpacker

as well as the jdk.pack module.

JEP 368. Text Blocks




At one time, Java had (among one and a half dozen other languages) an impact on Python. As Python is rapidly gaining popularity and is pushing confidently with other leaders of the Java and C IT charts, there’s nothing wrong with peeping at it. At one time, Java 9 introduced JShell, very similar to pythonium Jupiter. And now, the time has come for text blocks.

When we need to write a line, we write a line:

String s = "";

When we need to write a formatted string, we write something like this:
String oldHtml = "<html>\n\t<body>\n\t\t<p>Hi all!</p>\n\t</body>\n</html>";

The text is completely unreadable. And so, in Java 14 the problem is solved. Now, using triple quotation marks, you can write any text:

        String html = """
                      <html>
                          <body>
                              <p>Hi all!</p>
                          </body>
                      </html>
                      """;

It is much more convenient and readable. We can simply copy any text into the code and not bother with tabs and hyphens. The beauty! If we just need to transfer the text to another line in such a text without making a hyphen, we can use a new literal - the backslash. This symbol generalizes that the transference following it is not a transference. Example:

        String text = """
                    \
                 ,  , \
                     \
                   . \
                , ,  ! \
                  ; \
                    \
                   .
                """;

Conclusion:

The gods have still given you Golden days, golden nights, And languid virgins fixed on you attentive eyes. Play, sing, O friends! Lose a fleeting evening; And your joy of carelessness Through tears I smile.


JEP 370. External Memory Access API (Incubator)


It happens that an application accesses external memory like Ignite, mapDB, memcached, etc. The existing APIs for accessing it are quite working, but Oracle wanted something global. So the abstractions of MemorySegment, MemoryAddress and MemoryLayout appeared. While the feature is in the incubator, and everyone can still be content with ByteBuffer, Unsafe and JNI.

Conclusion


I don’t know about you, reader, but I like the six-month release cycle that Oracle has switched to since Java 9. Now Oracle does not set the task of releasing absolutely stable releases, but an experienced javist will have no difficulty in being aware of the stability of one feature or another, observing for their development and testing something from the incubator. The language has become more vibrant, changeable, very bold innovations and borrowings from other languages ​​began to appear in it. Yes, someone doesn’t keep up with the flickering of releases, but our profession is such that we need to keep up, so whether we want it or not we meet Java 14.

As usual, I am attaching a project on a github with code examples: [click here]

All Articles