StackWise
Modern JavaModern Java (17–21)

var, Switch Expressions & Text Blocks

The everyday Java 10–17 language improvements — local type inference, arrow switches that return values, and multi-line strings.

beginner 4 min readLesson 17 of 26

Java’s release cadence

Since Java 9, a new version ships every six months, with a Long-Term Support (LTS) release every two years: 11, 17, 21 and 25. Most production code targets an LTS. This track uses Java 21, and notes when something needs a later release.

Version Highlights
10 var
11 (LTS) String helpers, HttpClient, single-file java Main.java
14 Switch expressions, helpful NPE messages
15 Text blocks
16 Records, instanceof pattern matching, Stream.toList()
17 (LTS) Sealed classes
21 (LTS) Virtual threads, pattern matching for switch, record patterns, sequenced collections
25 (LTS) Flexible constructor bodies, compact source files, scoped values

var — local variable type inference (Java 10)

var lets the compiler infer a local variable’s type from its initialiser. The variable is still statically typed — var is not dynamic.

var orders = new HashMap<String, List<Order>>();   // HashMap<String, List<Order>>
var reader = Files.newBufferedReader(path);        // BufferedReader
for (var entry : orders.entrySet()) { ... }        // Map.Entry<String, List<Order>>
try (var in = new FileInputStream(file)) { ... }

Where var can’t be used: fields, method parameters, return types, without an initialiser (var x;), with null (var x = null;), or with an array initialiser (var a = {1, 2};).

Switch expressions (Java 14)

The new arrow form of switch doesn’t fall through, can return a value, and is exhaustive when used as an expression.

int days;
switch (month) {
    case FEBRUARY:
        days = leap ? 29 : 28;
        break;
    case APRIL:
    case JUNE:
    case SEPTEMBER:
    case NOVEMBER:
        days = 30;
        break;
    default:
        days = 31;          // forgetting a break here = bug
}
int days = switch (month) {
    case FEBRUARY -> leap ? 29 : 28;
    case APRIL, JUNE, SEPTEMBER, NOVEMBER -> 30;
    default -> 31;
};

Use yield to return from a block:

String category = switch (statusCode / 100) {
    case 2 -> "success";
    case 4 -> {
        log.warn("Client error {}", statusCode);
        yield "client-error";
    }
    case 5 -> "server-error";
    default -> "other";
};

Text blocks (Java 15)

A text block is a multi-line string literal delimited by """. Incidental indentation (the common leading whitespace) is stripped automatically, so you can indent it with your code.

ModernSyntax.javaJava 21
import java.time.*;
import java.util.*;

class Main {
    enum Status { DRAFT, SUBMITTED, APPROVED, REJECTED }

    static String badge(Status s) {
        return switch (s) {                 // exhaustive: no default needed for enums
            case DRAFT -> "⚪ draft";
            case SUBMITTED -> "🟡 pending review";
            case APPROVED -> "🟢 approved";
            case REJECTED -> "🔴 rejected";
        };
    }

    static int daysIn(Month m, boolean leap) {
        return switch (m) {
            case FEBRUARY -> leap ? 29 : 28;
            case APRIL, JUNE, SEPTEMBER, NOVEMBER -> 30;
            default -> 31;
        };
    }

    public static void main(String[] args) {
        var statuses = EnumSet.allOf(Status.class);     // var infers EnumSet<Status>
        for (var s : statuses) System.out.println(badge(s));

        System.out.println("Feb 2028 has " + daysIn(Month.FEBRUARY, Year.isLeap(2028)) + " days");

        var name = "Ada";
        var role = "admin";
        String json = """
            {
              "name": "%s",
              "role": "%s",
              "tags": ["java", "spring"]
            }
            """.formatted(name, role);
        System.out.print(json);

        String sql = """
            SELECT o.id, o.total \
            FROM orders o \
            WHERE o.status = 'PAID'""";          // \ joins lines: no newline
        System.out.println(sql);

        String poem = """
            Roses are red,\s
            Violets are blue""";                  // \s keeps a trailing space
        System.out.println(poem.lines().map(l -> "|" + l + "|").toList());
    }
}

Text block escapes:

  • \ at the end of a line — join with the next line (no newline)
  • \s — a single space that isn’t stripped as trailing whitespace
  • The closing """ position controls indentation: put it on its own line at the content’s indentation to keep a trailing newline

Pattern matching for instanceof (Java 16)

No more cast after an instanceof check:

// before
if (obj instanceof String) {
    String s = (String) obj;
    System.out.println(s.length());
}

// Java 16+
if (obj instanceof String s && !s.isBlank()) {
    System.out.println(s.length());
}

The binding s is in scope only where the match is definitely true. It is the gateway to the full pattern matching covered in Sealed Classes & Pattern Matching.

Finished this lesson?

Mark it complete to track your progress through Modern Java.