DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Related

  • Alternative Structured Concurrency
  • Jakarta EE 12: Entering the Data Age of Enterprise Java
  • Zero-Downtime Deployments for Java Apps on Kubernetes
  • Rethinking Java CRUDs With Event Sourcing and CQRS Patterns

Trending

  • Ingesting Fixed-Width Mainframe Files Into Delta Lake: The Details Nobody Writes Down
  • Building Enterprise-Grade Real-Time IoT Dashboards with Vue 3, MQTT, and Kafka
  • From AI Chaos to Control: Building Enterprise-Grade LLM Gateways With MuleSoft Anypoint
  • Ujorm3: A New Lightweight ORM for JavaBeans and Records
  1. DZone
  2. Coding
  3. Java
  4. New Features From Java 12 to 17

New Features From Java 12 to 17

A fast-forward tour through new, production-ready features when upgrading from Java 11 to Java 17.

By 
Nenad Jovanovic user avatar
Nenad Jovanovic
·
Aug. 26, 21 · Analysis
Likes (10)
Comment
Save
Tweet
Share
12.6K Views

Join the DZone community and get the full member experience.

Join For Free

Three years after Java 11 (the last long-term support version so far), Java 17 LTS will be released in September 2021. Time to take a quick tour through the new features that developers can enjoy after upgrading from 11 to 17. Note that many more improvements have been made under the hood - this article focuses on those features that can be directly used by most developers:

  • Switch Expressions (JEP 361)
  • Text Blocks (JEP 378)
  • Packaging Tool (JEP 392)
  • Pattern Matching for instanceof (JEP 394)
  • Records (JEP 395)
  • Sealed Classes (JEP 409)

Switch Expressions

The switch can now return a value, just like an expression:

Java
 
// assign the group of the given planet to a variable
String group = switch (planet) {
  case MERCURY, VENUS, EARTH, MARS -> "inner planet";
  case JUPITER, SATURN, URANUS, NEPTUNE -> "outer planet";
};

If the right-hand side of a single case requires more code, it can be written inside a block, and the value returned using yield:

Java
 
// print the group of the given planet, and some more info,
// and assign the group of the given planet to a variable
String group = switch (planet) {
  case EARTH, MARS -> {
    System.out.println("inner planet");
    System.out.println("made up mostly of rock");
    yield "inner";
  }
  case JUPITER, SATURN -> {
    System.out.println("outer planet");
    System.out.println("ball of gas");
    yield "outer";
  }
};

However, switching with the new arrow labels does not require returning a value, just like a void expression:

Java
 
// print the group of the given planet
// without returning anything
switch (planet) {
  case EARTH, MARS -> System.out.println("inner planet");
  case JUPITER, SATURN -> System.out.println("outer planet");
}

Compared to a traditional switch, the new switch expression

  • Uses “->” instead of “:”
  • Allows multiple constants per case
  • Does not have fall-through semantics (i.e., doesn’t require breaks)
  • Makes variables defined inside a case branch local to this branch

Moreover, the compiler guarantees switch exhaustiveness in that exactly one of the cases gets executed, meaning that either

  • All possible values are listed as cases (as with the above enum consisting of eight planets), or
  • A “default” branch has to be provided

Text Blocks

Text blocks allow writing multi-line strings that contain double quotes, without having to use \n or \" escape sequences:

Java
 
String block = """
  Multi-line text
   with indentation
    and "double quotes"!
  """;

A text block is opened by three double quotes """ followed by a line break, and closed by three double-quotes.

The Java compiler applies a smart algorithm to strip leading white space from the resulting string such that

  • The indentation that is relevant only for better readability of the Java source code is removed
  • The indentation relevant to the string itself remains untouched

In the above example, the resulting string looks as follows, where each . marks a white space:

Java
 
Multi-line.text
.with.indentation
..and."double.quotes"!

Imagine a vertical bar that spans the text block’s height, moving from left to right and deleting white spaces until it touches the first non-whitespace character. The closing text block delimiter also counts, so moving it two positions to the left

Java
 
String block = """
  Multi-line text
   with indentation
    and "double quotes"!
""";

Results in the following string:

Java
 
..Multi-line.text
...with.indentation
....and."double.quotes"!

In addition, trailing white space is removed from every line, which can be prevented by using the new escape sequence \s.

Line breaks inside text blocks can be escaped:

Java
 
String block = """
    No \
    line \
    breaks \
    at \
    all \
    please\
    """;

Results in the following string, without any line breaks:

No.line.breaks.at.all.please

Alternatively, the final line break can also be removed by appending the closing delimiter directly to the string’s end:

Java
 
String block = """
    No final line break
    at the end of this string, please""";

Inserting variables into a text block can be done as usual with the static method String::format, or with the new instance method String::formatted, which is a little shorter to write:

Java
 
String block = """
    %s marks the spot.
    """.formatted("X");

Packaging Tool

Suppose you have a JAR file demo.jar in a lib directory, along with additional dependency JARs. The following command

jpackage --name demo --input lib --main-jar demo.jar --main-class demo.Main

Packages up this demo application into a native format corresponding to your current platform:

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

The resulting package also contains those parts of the JDK that are required to run the application, along with a native launcher. This means that users can install, run, and uninstall the application in a platform-specific, standard way, without having to explicitly install Java beforehand.

Cross-compilation is not supported: If you need a package for Windows users, you must create it with jpackage on a Windows machine.

Package creation can be customized with many more options, which are documented on the jpackage man page.

Pattern Matching for Instanceof

Pattern matching for instanceof eliminates boilerplate code for performing casts after type comparisons:

Java
 
Object o = "string disguised as object";
if (o instanceof String s) {
  System.out.println(s.toUpperCase());
}

In the example above, the scope of the new variable s is intuitively limited to the if branch. To be precise, the variable is in scope where the pattern is guaranteed to have matched, which also makes the following code valid:

Java
 
if (o instanceof String s && !s.isEmpty()) {
  System.out.println(s.toUpperCase());
}

And also vice versa:

Java
 
if (!(o instanceof String s)) {
  throw new RuntimeException("expecting string");
}
// s is in scope here!
System.out.println(s.toUpperCase());

Records

Records reduce boilerplate code for classes that are simple data carriers:

record Point(int x, int y) { }

This one-liner results in a record class that automatically defines

  • Fields for x and y (both private and final)
  • A canonical constructor for all fields
  • Getters for all fields
  • equals, hashCode, and toString (taking all fields into account)
Java
 
// canonical constructor
Point p = new Point(1, 2);
    
// getters - without "get" prefix
p.x();
p.y();
    
// equals / hashCode / toString
p.equals(new Point(1, 2)); // true
p.hashCode();              // depends on values of x and y
p.toString();              // Point[x=1, y=2]

Some of the most important restrictions of record classes are that they

  • Are immutable (since their fields are private and final)
  • Are implicitly final
  • Cannot define additional instance fields
  • Always extend the Record class

However, it is possible to

  • Define additional methods
  • Implement interfaces
  • Customize the canonical constructor and accessors
Java
 
record Point(int x, int y) {

  // explicit canonical constructor
  Point {

    // custom validations
    if (x < 0 || y < 0) 
      throw new IllegalArgumentException("no negative points allowed");

    // custom adjustments (usually counter-intuitive)
    x += 1000;
    y += 1000;

    // assignment to fields happens automatically at the end

  }
  
  // explicit accessor
  public int x() {
    // custom code here...
    return this.x;
  }
  
}

Besides, it is possible to define a local record inside a method:

Java
 
public void withLocalRecord() {
  record Point(int x, int y) { };
  Point p = new Point(1, 2);
}

Sealed Classes

A sealed class explicitly lists the permitted direct subclasses. Other classes must not extend from this class:

Java
 
public sealed class Parent
  permits ChildA, ChildB, ChildC { ... }

Likewise, a sealed interface explicitly lists the permitted direct subinterfaces and implementing classes:

Java
 
sealed interface Parent
  permits ChildA, ChildB, ChildC { ... }

The classes or interfaces in the permits the list must be located in the same package (or in the same module if the parent is in a named module).

The permits the list can be omitted if the subclasses (or interfaces) are located inside the same file:

Java
 
public sealed class Parent {
  final class Child1 extends Parent {}
  final class Child2 extends Parent {}
  final class Child3 extends Parent {}
}

Every subclass or interface in the permits the list must use exactly one of the following modifiers:

  • final (disallows further extension; for subclasses only, since interfaces cannot be final)
  • sealed (permits further, limited extension)
  • non-sealed (permits unlimited extension again)
Java (programming language)

Published at DZone with permission of Nenad Jovanovic. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Alternative Structured Concurrency
  • Jakarta EE 12: Entering the Data Age of Enterprise Java
  • Zero-Downtime Deployments for Java Apps on Kubernetes
  • Rethinking Java CRUDs With Event Sourcing and CQRS Patterns

Partner Resources

×

Comments

The likes didn't load as expected. Please refresh the page and try again.

  • RSS
  • X
  • Facebook

ABOUT US

  • About DZone
  • Support and feedback
  • Community research

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 215
  • Nashville, TN 37211
  • [email protected]

Let's be friends:

  • RSS
  • X
  • Facebook