1. var for Local Variables (Java 10)
Idea. The type is inferred from the right-hand side, except for uninitialized local variables.
public class VarDemo {
public static void main(String[] args) {
var list = java.util.List.of("a", "b"); // List<String>
for (var s : list) {
System.out.println(s.toUpperCase());
}
var map = new java.util.HashMap<String, Integer>();
map.put("x", 1);
}
}Explanation. var doesn’t change the type, only makes the declaration shorter.
Errors. var x; — not allowed without initialization. var in fields — not allowed.
Best practices. Use it when the type is obvious from the RHS (new HashMap<>(), factories, stream operations).
2. Switch Expressions (Java 14 permanent; 12–13 preview)
Idea. switch returns a value, arrow functions ->, yield.
public class SwitchExprDemo {
enum Day { MON, SAT, SUN }
public static void main(String[] args) {
var d = Day.SAT;
int hours = switch (d) {
case SAT, SUN -> 0;
case MON -> 8;
default -> 6;
};
System.out.println(hours);
}
}Errors. Mixing old : and new -> in one block.
Best. Prefer the arrow syntax, no fall-through and break.
3. Text Blocks """...""" (Java 15)
Idea. Multiline strings without escaping.
public class TextBlocksDemo {
public static void main(String[] args) {
String json = """
{
"name": "Vitaliy",
"skills": ["Java","Spring"]
}
""";
System.out.println(json);
}Errors. Extra indentation — align to the “common indentation” of the closing """.
Best. For SQL/JSON/XML — readability + formatted(...) for substitutions.
4. Records (Java 16)
Idea. Compact immutable DTO: equals/hashCode/toString and accessors are generated.
public record User(String username, int level) {
public User {
if (level < 0) throw new IllegalArgumentException("level >= 0");
}
public String display() { return username + ":" + level; }
}Errors. Mutation via reflection/non-standard tricks — breaks the idea of records. Best. Store “data without state”, validation — in the compact constructor.
5. Pattern Matching for instanceof (Java 16)
Idea. Pattern matching immediately gives you a variable of the correct type.
static int len(Object o) {
if (o instanceof String s) return s.length();
if (o instanceof java.util.Collection<?> c) return c.size();
return -1;
}Errors. Shadowing variables: don’t redefine the name s in the inner scope.
Best. Remove casts: less noise and NPEs.
6. Sealed Classes/Interfaces (Java 17)
Idea. Limited polymorphism: who can inherit.
public sealed interface Shape permits Circle, Rect { }
public final class Circle implements Shape {
public final double r;
public Circle(double r) { this.r = r; }
}
public non-sealed class Rect implements Shape { // can inherit freely further
public final double w, h;
public Rect(double w, double h) { this.w = w; this.h = h; }
}Errors. Forget permits (if not all in one file/package).
Best. Combine with switch + patterns — exhaustive branches.
7. Pattern Matching for switch + Guarded Patterns (Java 21 permanent)
Idea. switch by types and conditions, exhaustive checks.
static String render(Object o) {
return switch (o) {
case null -> "null";
case String s when s.isBlank() -> "empty string";
case String s -> "str: " + s;
case Integer i && i > 0 -> "pos int: " + i;
case java.util.List<?> lst -> "list size=" + lst.size();
default -> "other";
};
}Errors. Uncovered cases (add default/null).
Best. For sealed hierarchies, default is often not needed — the compiler checks for completeness.
8. Record Patterns (Java 21 permanent)
Idea. Unpack record directly into variables/switch branches.
record Point(int x, int y) {}
static int manhattan(Object o) {
return switch (o) {
case Point(int x, int y) -> Math.abs(x) + Math.abs(y);
default -> -1;
};
}Errors. Mismatch of arity/order of components.
Best. Use together with sealed for clean pattern-based code.
9. Unnamed Variables & Patterns (_) (Java 22 permanent)
Idea. _ as “I consciously ignore”.
public class UnderscoreDemo {
public static void main(String[] _ /* ignores arguments */) {
Object o = "java";
if (o instanceof String _) { // matched, but value is not needed
System.out.println("It is String");
}
}Errors. _ cannot be used as a regular variable name.
Best. Mark “unused” parameters/patterns to avoid dummy variables.
10. Compact Source Files & Instance main (Java 25, final branch unnamed/implicit)
Idea. Mini-programs without an explicit class in the source; main is not static.
void main() { System.out.println("Hello, Java 25!"); }The compiler automatically “wraps” it in a class during compilation.
Errors. Use both a regular class and a “compact” style in the same file. Best. Excellent for examples, tools, snippets.
11. Flexible Constructor Bodies (Java 25 permanent)
Idea. You can execute code before super(...)/this(...) (with restrictions on order and initialization).
class Base {
Base(String id) { System.out.println("Base " + id); }
}
class Child extends Base {
Child(String raw) {
String id = raw.trim().toUpperCase(); // logic before super(...)
super(id);
System.out.println("Child ready");
}
}Errors. Referencing this/overridden methods before correct initialization — avoid side effects.
Best. Keep early logic clean/deterministic (parsing, validation, normalization).
12. Module System (Java 9) — only language syntax
Idea. module-info.java describes the boundaries.
module com.example.core {
exports com.example.api;
requires java.sql;
}Errors. “Grey” dependencies, forgotten exports.
Best. Explicitly fix the public surface through exports/opens.
13. Module Import Declarations (Java 25 permanent)
Idea. Import the entire module “into the visibility space” of the source.
import module java.base;
import module java.sql;
public class ModuleImportDemo {
System.out.println(java.time.Instant.now());
try (var conn = java.sql.DriverManager.getConnection("jdbc:h2:mem:db")) {
System.out.println(conn.getMetaData().getURL());
} catch (Exception e) {
e.printStackTrace();
}
}Errors. Confusing with import package.*; — that’s a different mechanism (module level).
Best. Use in modular applications for explicit dependencies in the sources.
14. Primitives in Patterns/instanceof/switch (Java 25 — still preview)
Idea. Match on primitives without boxing.
// Compile with --enable-preview --release 25
static String describeInt(int x) {
return switch (x) {
case int i && i > 0 -> "positive";
case 0 -> "zero";
default -> "negative";
};
}Errors. Forget --enable-preview during compilation/run.
Best. Experiment selectively; monitor finalization in 26+.
Common Migration Errors (8 → 25)
- Confusing switch expression (needs a value) and the old
switch(operator). - Using
recordas a “god object” with mutable fields via mutator tricks — breaks the contract. sealed+switch: forgetting to add new subclasses — the compiler will complain about uncovered cases.- Text Blocks with a “ladder” of indentation — align the closing quotes.
Style Recommendations
- Include pattern matching for branching by type/shape of data — fewer casts and
if-else. - For DTOs — records; for hierarchies with a closed set — sealed.
- For template text — text blocks; for computed text — switch expressions +
formatted(...). - Demonstrate new learning/CLI snippets in the style of Compact Source Files (Java 25).