Java Evolution — Part 5

Java 12 (March 19, 2019) and Java 13 (September 17, 2019) were non-LTS releases that served as incubators for features that would be finalised in Java 14 and 15. The most significant previews — switch expressions and text blocks — fundamentally changed how Java developers write everyday code.


Switch Expressions (Preview — Java 12 & 13)

The traditional switch statement in Java had two well-known problems. First, it was a statement, not an expression — it could not return a value, so you needed a mutable variable to capture the result. Second, it had fall-through semantics by default, meaning a missing break would silently execute the next case.

Java 12 (JEP 325) introduced switch expressions as a preview feature, and Java 13 (JEP 354) refined them with the yield keyword.

The Old Switch Statement

// Before Java 12 — verbose, fall-through prone
String dayType;
switch (day) {
case MONDAY:
case TUESDAY:
case WEDNESDAY:
case THURSDAY:
case FRIDAY:
dayType = "Weekday";
break;
case SATURDAY:
case SUNDAY:
dayType = "Weekend";
break;
default:
throw new IllegalArgumentException("Unknown day: " + day);
}

The New Switch Expression (Arrow Form)

// Java 12+ — switch as an expression with arrow labels
String dayType = switch (day) {
case MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY -> "Weekday";
case SATURDAY, SUNDAY -> "Weekend";
};

The arrow (->) form has no fall-through. Each case is independent. Multiple labels can be grouped with commas. The entire switch is an expression that produces a value.

The yield Keyword (Java 13)

When a case arm needs to execute multiple statements before producing a value, you use a block with yield:

int numLetters = switch (day) {
case MONDAY, FRIDAY, SUNDAY -> 6;
case TUESDAY -> 7;
case THURSDAY, SATURDAY -> 8;
case WEDNESDAY -> {
System.out.println("Computing letters for Wednesday...");
yield 9; // yield produces the value from a block
}
};

yield is a context-sensitive keyword — it is only special inside a switch block. You can still use yield as a variable or method name elsewhere.

Exhaustiveness

Switch expressions must be exhaustive — every possible value of the selector must be handled. For enums, the compiler verifies this at compile time. For other types, a default case is required.

// Compiler error if any Day enum constant is missing and no default
String result = switch (day) {
case MONDAY -> "Start of the week";
case FRIDAY -> "End of the week";
// Missing cases — compiler error!
};

Text Blocks (Preview — Java 13)

JEP 355 introduced text blocks as a preview feature in Java 13. A text block is a multi-line string literal delimited by triple quotes ("""). It eliminates the need for explicit \n characters and string concatenation when embedding multi-line content like JSON, HTML, SQL, or XML.

The Problem with Traditional String Literals

// Before Java 13 — painful multi-line strings
String html = "<html>\n" +
" <body>\n" +
" <p>Hello, world</p>\n" +
" </body>\n" +
"</html>\n";
String json = "{\n" +
" \"name\": \"Alice\",\n" +
" \"age\": 30\n" +
"}";
String sql = "SELECT u.name, u.email\n" +
"FROM users u\n" +
"WHERE u.active = true\n" +
"ORDER BY u.name";

Text Blocks in Java 13

// Java 13 — text blocks
String html = """
<html>
<body>
<p>Hello, world</p>
</body>
</html>
""";
String json = """
{
"name": "Alice",
"age": 30
}
""";
String sql = """
SELECT u.name, u.email
FROM users u
WHERE u.active = true
ORDER BY u.name
""";

Indentation Handling

The Java compiler automatically strips incidental whitespace — the common leading whitespace shared by all lines. The position of the closing """ determines the indentation baseline.

// The closing """ on its own line sets the baseline to column 0
String a = """
hello
world
""";
// a = "hello\nworld\n"
// The closing """ indented sets the baseline to that indentation
String b = """
hello
world
""";
// b = " hello\n world\n"

Escape Sequences in Text Blocks

Text blocks support two new escape sequences (finalised in Java 15):

// \s — explicit trailing space (prevents stripping)
String padded = """
red \s
green\s
blue \s
""";
// \ at end of line — line continuation (no newline in output)
String single = """
This is a very long string that \
continues on the next line.
""";
// single = "This is a very long string that continues on the next line.\n"

Teeing Collector (Java 12)

Collectors.teeing() (JEP 12) allows a stream to be processed by two independent collectors simultaneously, with the results merged by a function. This avoids iterating the stream twice.

// Calculate min and max in a single pass
record MinMax(int min, int max) {}
MinMax result = Stream.of(3, 1, 4, 1, 5, 9, 2, 6)
.collect(Collectors.teeing(
Collectors.minBy(Integer::compareTo),
Collectors.maxBy(Integer::compareTo),
(min, max) -> new MinMax(
min.orElseThrow(),
max.orElseThrow()
)
));
System.out.println(result); // MinMax[min=1, max=9]

Another practical example — compute both the sum and count in one pass to calculate an average:

double average = Stream.of(10, 20, 30, 40, 50)
.collect(Collectors.teeing(
Collectors.summingInt(Integer::intValue),
Collectors.counting(),
(sum, count) -> (double) sum / count
));
// 30.0

Compact Number Formatting (Java 12)

NumberFormat.getCompactNumberInstance() formats numbers in a human-readable compact form:

NumberFormat shortFmt = NumberFormat.getCompactNumberInstance(
Locale.US, NumberFormat.Style.SHORT
);
System.out.println(shortFmt.format(1_000)); // 1K
System.out.println(shortFmt.format(1_500_000)); // 2M
System.out.println(shortFmt.format(1_200_000_000));// 1B
NumberFormat longFmt = NumberFormat.getCompactNumberInstance(
Locale.US, NumberFormat.Style.LONG
);
System.out.println(longFmt.format(1_500_000)); // 2 million

String Methods (Java 12)

Java 12 added String.indent(n) and String.transform(f):

// indent — add or remove leading whitespace from each line
String indented = "hello\nworld".indent(4);
// " hello\n world\n"
// transform — apply a function to the string (useful in method chains)
String result = " hello "
.transform(String::strip)
.transform(String::toUpperCase);
// "HELLO"

Summary

Java 12 and 13 were stepping stones. The features they introduced in preview — switch expressions and text blocks — would become two of the most-used features in modern Java.

FeatureVersionKey Benefit
Switch Expressions (Preview)12 & 13Expressions, no fall-through, arrow syntax
yield keyword13Multi-statement switch expression arms
Text Blocks (Preview)13Multi-line strings without escape noise
Teeing Collector12Two collectors in one stream pass
Compact Number Formatting12Human-readable number display
String.indent() / transform()12Indentation control and functional string chains