Complete Reference for Desktop | Print-friendly version for desk reference
This is Part 6 (final) of "Java 17 Features Every Senior Developer Should Know" - a comprehensive reference covering syntax, usage patterns, feature interactions, and migration strategies for all 6 features covered in Parts 1-5. Perfect for printing and keeping at your desk as you work with modern Java code.
How to Use This Series
First time learning Java 17 features?
Need a quick reference?
Use the Quick Reference Cards below (this page)
Each section provides syntax, examples, and use cases at a glance
Links to full parts for detailed explanations
Looking for specific feature?
Want to understand feature interactions?
- See full articles for detailed explanations and real-world patterns
Planning a migration?
- See full articles for phased migration strategy and compatibility notes
Series Overview & Timeline
This series covers 6 major Java features spanning Java 10-17, representing over 20 years of language evolution:
| Part | Feature | Release | JEP |
| 1 | var - Local variable type inference | Java 10 | 286 |
| 2 | Records - Immutable data carriers | Java 16 | 395 |
| 3 | Sealed Classes - Controlled inheritance | Java 17 | 409 |
| 4 | Pattern Matching - Type check + cast | Java 16 | 394 |
| 5 | Switch Expressions - Returns values | Java 14 | 361 |
| 6 | Text Blocks - Multi-line strings | Java 15 | 378 |
Why These Features Matter:
Part 1-2: Reduce boilerplate (less code to write)
Part 3-4: Increase safety (compiler catches more errors)
Part 5-6: Improve readability (code matches intent)
Feature Comparison Matrix
Decision At-a-Glance
| Challenge | Solution | When | Avoid |
| Long generic types | var | Type obvious from right side | Type unclear from context |
| Immutable data classes | Records | DTOs, value objects | Mutable data, need inheritance |
| Controlled inheritance | Sealed classes | Fixed type hierarchies | Open-for-extension APIs |
| Type checking + casting | Pattern matching | Checking types with immediate use | Polymorphism is better choice |
| Returning from switch | Switch expressions | Enum mapping, value computation | Simple if-else, need fall-through |
| Multi-line strings | Text blocks | JSON, SQL, HTML, XML | Single line strings |
Feature Dependencies
var (Java 10)
↓ (enables clean declaration of)
Records (Java 16)
↓ (work well with)
Sealed Classes (Java 17)
↓ (enables exhaustive checking with)
Pattern Matching (Java 16)
↓ (used in)
Switch Expressions (Java 14)
↓ (format data with)
Text Blocks (Java 15)
Quick Reference Cards
var Keyword (Java 10)
JEP 286 | Local variable type inference
📖 See also: Part 1: Introduction & var Keyword - Full feature deep dive with examples and pitfalls
Syntax
var name = expression;
✅ Use When
Type is obvious from right side: var user = new User()
Long generic types: var map = new HashMap<String, List<Employee>>()
Stream chains: var filtered = list.stream().filter(...)
For-loops: for (var item : items)
❌ Don't Use When
Type isn't clear: var data = fetchData() (what type?)
Need supertype: List<String> list = new ArrayList<>() (flexibility)
Fields, parameters, return types (not allowed)
Lambda/method reference alone: var c = String::length (error)
Examples
var message = "Hello";
var count = 42;
var users = List.of("Alice", "Bob");
var map = new HashMap<String, Integer>();
var x = getData();
var y = null;
var list = new ArrayList<>();
Quick Rules
Records (Java 16)
JEP 395 | Immutable data carriers
📖 See also: Part 2: Records - Full feature deep dive with examples and best practices
Syntax
record Name(Type1 field1, Type2 field2) {
}
✅ Use When
Data Transfer Objects (DTOs)
Value objects (Point, Money, Range)
Configuration objects
API request/response models
Return values from methods (multiple values)
❌ Don't Use When
Need mutability
Need inheritance (records are final)
Want to hide internal structure
Need JavaBeans getters (use field() not getField())
Complex lazy initialization
Examples
record Point(int x, int y) {}
record Range(int start, int end) {
public Range {
if (start > end) throw new IllegalArgumentException();
}
}
record Person(String name, int age) {
public boolean isAdult() { return age >= 18; }
public Person withAge(int newAge) { return new Person(name, newAge); }
}
record Pair<T, U>(T first, U second) {}
record Circle(double radius) implements Shape {
@Override
public double area() { return Math.PI * radius * radius; }
}
What You Get Automatically
Constructor: public Point(int x, int y)
Accessors: p.x(), p.y() (NOT getX(), getY())
equals(), hashCode(), toString()
Fields are private final
Class is final
Quick Rules
Sealed Classes (Java 17)
JEP 409 | Controlled inheritance
📖 See also: Part 3: Sealed Classes - Design patterns and hierarchy control explained
Syntax
sealed interface/class Name permits SubType1, SubType2 {
}
✅ Use When
Closed type hierarchies (known subtypes only)
Domain modeling with fixed alternatives
Exhaustive pattern matching
API design where you control all implementations
❌ Don't Use When
Examples
sealed interface Shape permits Circle, Rectangle, Triangle {}
record Circle(double radius) implements Shape {}
record Rectangle(double width, double height) implements Shape {}
record Triangle(double base, double height) implements Shape {}
sealed class Result<T> permits Success, Failure {}
final class Success<T> extends Result<T> {
private final T value;
}
final class Failure<T> extends Result<T> {
private final String error;
}
sealed interface Payment permits CreditCard, DebitCard, ExtensiblePayment {}
final class CreditCard implements Payment {}
final class DebitCard implements Payment {}
non-sealed interface ExtensiblePayment extends Payment {}
Permitted Subtypes Must
Be in same module (or same package if unnamed module)
Directly extend/implement the sealed type
Be declared as: final, sealed, or non-sealed
Quick Rules
Compiler knows all subtypes
Enables exhaustive pattern matching
Subtypes must be declared in permits clause
Better than marker interfaces for closed sets
Pattern Matching (Java 16)
JEP 394 | Pattern matching for instanceof
📖 See also: Part 4: Pattern Matching & Switch Expressions - Type patterns and exhaustiveness checking
Syntax
if (obj instanceof Type variableName) {
}
✅ Use When
Type checking with immediate use
Implementing equals() methods
Polymorphic dispatch
Guard conditions: obj instanceof String s && s.length() > 5
❌ Don't Use When
Polymorphism would be better (add methods to types)
Type checking indicates design smell
Performance is absolutely critical
Examples
if (obj instanceof String s) {
System.out.println(s.toUpperCase());
}
if (obj instanceof String s && s.length() > 0) {
return s.toUpperCase();
}
@Override
public boolean equals(Object obj) {
return obj instanceof Point p && this.x == p.x && this.y == p.y;
}
if (shape instanceof Circle c) {
return Math.PI * c.radius() * c.radius();
} else if (shape instanceof Rectangle r) {
return r.width() * r.height();
}
Scope Rules
if (obj instanceof String s && s.length() > 5) {
}
if (!(obj instanceof String s)) {
} else {
}
Quick Rules
Pattern variable scoped by flow analysis
instanceof returns false for null (safe)
No manual cast needed
Combines type check + cast + assignment
Switch Expressions (Java 14)
JEP 361 | Switch as expression with arrow syntax
📖 See also: Part 4: Pattern Matching & Switch Expressions - Clean conditional logic with yield
Syntax
var result = switch (value) {
case LABEL1 -> expression1;
case LABEL2, LABEL3 -> expression2;
default -> expressionN;
};
var result = switch (value) {
case LABEL1 -> {
yield value1;
}
};
✅ Use When
Mapping enum values
Returning values from branches
Exhaustive enum matching (no default needed)
Multi-way conditional logic
❌ Don't Use When
Simple if-else would suffice
Need fall-through (use colon syntax)
Cases are complex and unrelated
Examples
String dayType = switch (day) {
case MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY -> "Weekday";
case SATURDAY, SUNDAY -> "Weekend";
};
int result = switch (op) {
case ADD -> {
System.out.println("Adding");
yield a + b;
}
case SUBTRACT -> {
System.out.println("Subtracting");
yield a - b;
}
default -> throw new IllegalArgumentException();
};
int days = switch (season) {
case SPRING, SUMMER, FALL, WINTER -> 3;
};
Quick Rules
Arrow -> prevents fall-through
Must be exhaustive (all cases or default)
Use yield for multi-statement branches
No break needed with arrows
Can mix with pattern matching (future Java)
Text Blocks (Java 15)
JEP 378 | Multi-line string literals
📖 See also: Part 5: Text Blocks - Multi-line strings with security considerations
Syntax
String text = """
content here
more content
""";
✅ Use When
❌ Don't Use When
Examples
String json = """
{
"name": "Alice",
"age": 30,
"email": "alice@example.com"
}""";
String sql = """
SELECT u.name, u.email, o.total
FROM users u
JOIN orders o ON u.id = o.user_id
WHERE o.status = 'COMPLETED'
ORDER BY o.created_at DESC""";
String json = """
{
"name": "%s",
"age": %d
}""".formatted(name, age);
String text = """
Line 1
Indented line 2
Line 3
""";
String long = """
This is a long line that \
continues on the next line \
in source code.
""";
String trailing = """
Line with space:\s
""";
Special Escapes
\s - Essential space (survives whitespace stripping)
\ - Line continuation (no newline inserted)
Standard escapes work: \n, \t, \", \\
Quick Rules
Opening """ must be followed by newline
Closing """ position determines indent
All line endings normalized to \n
Trailing whitespace auto-removed
Common indent automatically stripped
End of Series
This concludes "Java 17 Features Every Senior Developer Should Know"
Series Navigation