Just take some time to refresh my Java knowledge, especially the new features after Java 8.

1. JEP 413: Code Snippets in Java API Documentation (@18) Link to heading

To replace the use of <pre>{@code ...}</pre> blocks.

/**
 * JEP 413: Code Snippets in Java API Documentation
 * The following code shows how to use {@code Optional.isPresent}:
 * {@snippet :
 * if (v.isPresent()) {
 *     System.out.println("v: " + v.get());
 * }
 * }
 */

2. JEP 400: UTF-8 by Default (@18) Link to heading

Specify UTF-8 as the default charset of the standard Java APIs. With this change, APIs that depend upon the default charset will behave consistently across all implementations, operating systems, locales, and configurations.

If a charset argument is not passed, then standard Java APIs typically use the default charset. The JDK chooses the default charset at startup based upon the run-time environment: the operating system, the user’s locale, and other factors.

Because the default charset is not the same everywhere, APIs that use the default charset pose many non-obvious hazards, even to experienced developers.

3. JEP 409: Sealed Classes (@17) Link to heading

A sealed class or interface can be extended or implemented only by those classes and interfaces permitted to do so.

A sealed class imposes three constraints on its permitted subclasses:

  • The sealed class and its permitted subclasses must belong to the same module, and, if declared in an unnamed module, to the same package.
  • Every permitted subclass must directly extend the sealed class.
  • Every permitted subclass must use a modifier:
    • final: prevent from being extended further
    • sealed: allow to be extended further
    • non-sealed: being open for extension by unknown subclasses
public abstract sealed class Shape
        permits Circle, Rectangle, Square {
}

public final class Circle extends Shape {
}

public sealed class Rectangle extends Shape
    permits FilledRectangle, TransparentRectangle{
}

public final class Square extends Shape {
}

public final class FilledRectangle extends Rectangle {
}

public final class TransparentRectangle extends Rectangle {
}

4. JEP 395: Records (@16) Link to heading

Record classes are a new kind of class in the Java language. Record classes help to model plain data aggregates with less ceremony than normal classes.

Rules for Record classes:

  • A record class cannot explicitly extend any class
  • A record class is implicitly final, and cannot be abstract.
  • The fields derived from the record components are final.
  • A record class cannot explicitly declare instance fields, and cannot contain instance initializers.
record Point(int x, int y) {
    public int square() {
        return x * y;
    }
}

record Range(int lo, int hi) {
    Range {
        if (lo > hi)
            throw new IllegalArgumentException(String.format("(%d,%d)", lo, hi));
    }
}

5. JEP 394: Pattern Matching for instanceof (@16) Link to heading

If obj is an instance of String, then it is cast to String and the value is assigned to the variable s.

if (obj instanceof String s) {
    // Let pattern matching do the work!
    ...
}

A pattern variable is only in scope where the compiler can deduce that the pattern has definitely matched and the variable will have been assigned a value.

if (a instanceof Point p) {
    // p is in scope
    ...
}
// p not in scope here
if (b instanceof Point p) {     // Sure!
        ...
}

The use of pattern matching in instanceof should significantly reduce the overall number of explicit casts in Java programs.

public final boolean equals(Object o) {
    return (o instanceof CaseInsensitiveString cis) &&
        cis.s.equalsIgnoreCase(s);
}

6. JEP 378: Text Blocks (@15) Link to heading

A text block is a multi-line string literal that avoids the need for most escape sequences, automatically formats the string in a predictable way, and gives the developer control over the format when desired.

The opening delimiter is a sequence of three double quote characters (""") followed by zero or more white spaces followed by a line terminator. The content begins at the first character after the line terminator of the opening delimiter.

The closing delimiter is a sequence of three double quote characters. The content ends at the last character before the first double quote of the closing delimiter.

String html = """
        <html>
            <body>
                <p>Hello, world</p>
            </body>
        </html>
        """;
System.out.println(html);

String query = """
        SELECT "EMP_ID", "LAST_NAME" FROM "EMPLOYEE_TB"
        WHERE "CITY" = 'INDIANAPOLIS'
        ORDER B
        """;
System.out.println(query);

7. New String methods Link to heading

  • formatted(), stripIndent, translateEscape(): @15
 // formatted(): equivalent to String.format()
String formatted = "1. %s 2. %s 3. %s ".formatted("one", "two", "three");
System.out.println(formatted);

// stripIndent(): remove incidental white spaces from the beginning and end of every line
String stripIndent = " Good day!   ".stripIndent();
String html = """
        <html>
            <body>
                <p>Hello, world</p>
            </body>
        </html>
            """.stripIndent();
System.out.println(stripIndent);
System.out.println(html);

String str = "This is tab \t, Next New Line \n,next backspace \b,next Single Quotes \' next,Double Quotes \" ";
System.out.println(str.translateEscapes());
  • indent() and transform(): @12
// indent(n): insert n spaces before each line after `lines()`
String multilineStr = "This is\na multiline\nstring.";
System.out.println(multilineStr.indent(5));

// transform: apply a single argument function to the string
String concat = "hello".transform(p -> p + " world");
Integer parsedInt = "45".transform(Integer::parseInt);
System.out.println(concat);
System.out.println(parsedInt);

8. JEP 361: Switch Expressions @14 Link to heading

In addition to traditional “case L :” labels in a switch block, we define a new simplified form, with “case L ->” labels. If a label is matched, then only the expression or statement to the right of the arrow is executed; there is no fall through.

switch (n) {
    case 1 -> System.out.println("one");
    case 2 -> System.out.println("two");
    default -> System.out.println("many");
}

String value = switch (n) {
    case 1 -> "one";
    case 2 -> "two";
    default -> "many";
};

In the event that a full block is needed, we introduce a new yield statement to yield a value, which becomes the value of the enclosing switch expression.

int j = switch (n) {
    case 0, 1 -> 0;
    case 2, 3, 4 -> 1;
    default -> {
        int result = new Random().nextInt();
        yield result;
    }
};

9. JEP 286: Local-Variable Type Inference @10 Link to heading

For local variable declarations with initializers, enhanced for-loop indexes, and index variables declared in traditional for loops, allow the reserved type name var to be accepted in place of manifest types:

It would not be available for method formals, constructor formals, method return types, fields, catch formals, or any other kind of variable declaration.

var list = Arrays.asList("A", "B", "C");   // infers ArrayList<String>
var stream = list.stream();     // infers Stream<String>

10. JEP 269: Convenience Factory Methods for Collections @9 Link to heading

Provide static factory methods on the List, Set, and Map interfaces for creating unmodifiable instances of those collections.

var set1 = Set.of(1, 2, 3, 3, 5, 2, 10);
var list1 = List.of("A", "Grade", "Morning", "A");

var set1 = Set.of(1, 2, 3, 3, 5, 2, 10);
var list1 = List.of("A", "Grade", "Morning", "A");

11. Interface private methods @9 Link to heading

From Java 9, private methods can be added to interfaces in Java.

interface Animal {
    default void bark() {
        think();
        System.out.println("hello");
    }

    private void think() {
        System.out.println("thinking...");
    }
}

12. New Optional Methods @9 Link to heading

// ifPresentOrElse()
Optional<String> nonEmpty = Optional.of("Hello World");
nonEmpty.ifPresentOrElse(System.out::println, () -> System.out.println("Empty"));

// or()
Optional<String> empty = Optional.empty();
nonEmpty.or(() -> Optional.of("Default")).ifPresent(System.out::println);
empty.or(() -> Optional.of("Default")).ifPresent(System.out::println);

// stream()
var fruits = List.of("Apple", "Orange", "Pear", "Banana");
fruits.stream().map(NewOptionalMethods::transform)
        .flatMap(Optional::stream)
        .forEach(System.out::println);

// isEmpty()
System.out.println(nonEmpty.isEmpty());
System.out.println(empty.isEmpty());

// orElseThrow()
nonEmpty.orElseThrow();
empty.orElseThrow();

The playground source code of this blog can be found here.