1. The Hook (The "Byte-Sized" Intro)
- In a Nutshell: Modern Java APIs (Java 9-21) add convenience methods.
- Collection factories (Java 9): List.of(), Set.of(), Map.of() create immutable collections (no nulls).
- Stream enhancements: takeWhile/dropWhile() (Java 9), toList() (Java 16).
- String methods: isBlank(), lines(), strip(), repeat() (Java 11).
- Optional improvements: ifPresentOrElse(), or(), isEmpty() (Java 9-11).
- Benefits: Less boilerplate, functional style, immutability by default.
- Key: Modern APIs favor immutability + fluent design!
Think of smartphone updates. Java 8 = basic phone. Modern Java = new shortcuts, widgets, gestures. Same phone, but smarter APIs (List.of() = quick contacts widget, isBlank() = smart text detection). Faster workflows!
2. Conceptual Clarity (The "Simple" Tier)
💡 The Analogy
- Old APIs: Manual transmission car (more control, more work)
- Modern APIs: Automatic car (convenience features, same destination)
3. Technical Mastery (The "Deep Dive")
Modern API Timeline
| Java Version | Key APIs |
|---|---|
| Java 9 | Collection factories, Stream takeWhile/dropWhile, Optional improvements |
| Java 10 | Optional.orElseThrow() |
| Java 11 | String isBlank/lines/strip/repeat, Optional.isEmpty() |
| Java 16 | Stream.toList() |
| Java 21 | Sequenced collections |
4. Interactive & Applied Code
java
import java.util.*;
import java.util.stream.*;
public class ModernAPIDemo {
public static void main(String[] args) {
demonstrateCollectionFactories();
demonstrateStreamEnhancements();
demonstrateStringMethods();
demonstrateOptionalImprovements();
}
// Collection Factories (Java 9)
static void demonstrateCollectionFactories() {
System.out.println("=== COLLECTION FACTORIES ===");
// ❌ BEFORE: Verbose
List<String> oldList = new ArrayList<>();
oldList.add("Alice");
oldList.add("Bob");
oldList = Collections.unmodifiableList(oldList);
// ✅ AFTER: Concise, immutable
List<String> list = List.of("Alice", "Bob", "Carol");
Set<String> set = Set.of("A", "B", "C");
Map<String, Integer> map = Map.of(
"Alice", 30,
"Bob", 25,
"Carol", 28
);
System.out.println("List: " + list);
System.out.println("Set: " + set);
System.out.println("Map: " + map);
// Immutable (throws UnsupportedOperationException)
try {
list.add("David");
} catch (UnsupportedOperationException e) {
System.out.println("Cannot modify: " + e.getMessage());
}
// No nulls allowed
try {
List.of("A", null, "C");
} catch (NullPointerException e) {
System.out.println("No nulls: " + e.getMessage());
}
// Map with many entries
Map<String, Integer> bigMap = Map.ofEntries(
Map.entry("key1", 1),
Map.entry("key2", 2),
Map.entry("key3", 3)
);
}
// Stream API Enhancements
static void demonstrateStreamEnhancements() {
System.out.println("\n=== STREAM ENHANCEMENTS ===");
// takeWhile() - take until condition false (Java 9)
List<Integer> numbers = List.of(1, 2, 3, 4, 1, 2);
List<Integer> taken = numbers.stream()
.takeWhile(n -> n < 4)
.collect(Collectors.toList());
System.out.println("takeWhile(<4): " + taken); // [1, 2, 3]
// dropWhile() - drop until condition false (Java 9)
List<Integer> dropped = numbers.stream()
.dropWhile(n -> n < 4)
.collect(Collectors.toList());
System.out.println("dropWhile(<4): " + dropped); // [4, 1, 2]
// toList() - direct to immutable list (Java 16)
List<Integer> list = numbers.stream()
.filter(n -> n % 2 == 0)
.toList(); // ✅ Shorter than collect(Collectors.toList())
System.out.println("toList: " + list);
// Stream.ofNullable() - handle nullable (Java 9)
String value = null;
long count = Stream.ofNullable(value).count();
System.out.println("ofNullable count: " + count); // 0
// iterate() with predicate (Java 9)
List<Integer> limited = Stream.iterate(0, n -> n < 10, n -> n + 1)
.toList();
System.out.println("iterate: " + limited);
}
// String Methods (Java 11+)
static void demonstrateStringMethods() {
System.out.println("\n=== STRING METHODS ===");
// isBlank() - checks if blank (whitespace-only or empty)
System.out.println("' '.isBlank(): " + " ".isBlank()); // true
System.out.println("'hi'.isBlank(): " + "hi".isBlank()); // false
// lines() - split by line terminators
String multiline = "Line 1\nLine 2\nLine 3";
multiline.lines().forEach(System.out::println);
// strip() - remove leading/trailing whitespace (Unicode-aware)
String padded = " hello ";
System.out.println("strip: [" + padded.strip() + "]");
System.out.println("stripLeading: [" + padded.stripLeading() + "]");
System.out.println("stripTrailing: [" + padded.stripTrailing() + "]");
// repeat() - repeat string n times
System.out.println("repeat: " + "Hi".repeat(3)); // HiHiHi
// indent() - add/remove indentation (Java 12)
String text = "Hello\nWorld";
System.out.println("indent(4):\n" + text.indent(4));
// transform() - apply function (Java 12)
String result = "hello".transform(s -> s.toUpperCase() + "!");
System.out.println("transform: " + result);
// formatted() - like String.format (Java 15)
String formatted = "Name: %s, Age: %d".formatted("Alice", 30);
System.out.println("formatted: " + formatted);
}
// Optional Enhancements
static void demonstrateOptionalImprovements() {
System.out.println("\n=== OPTIONAL ENHANCEMENTS ===");
// ifPresentOrElse() - consumer + runnable (Java 9)
Optional<String> opt = Optional.of("Present");
opt.ifPresentOrElse(
value -> System.out.println("Value: " + value),
() -> System.out.println("Empty")
);
// or() - lazy alternative Optional (Java 9)
Optional<String> empty = Optional.empty();
Optional<String> result = empty.or(() -> Optional.of("Default"));
System.out.println("or: " + result.get());
// isEmpty() - opposite of isPresent (Java 11)
System.out.println("isEmpty: " + empty.isEmpty());
// orElseThrow() - no-arg version (Java 10)
try {
empty.orElseThrow();
} catch (NoSuchElementException e) {
System.out.println("orElseThrow: " + e.getClass().getSimpleName());
}
// stream() - convert to Stream (Java 9)
long count = Optional.of("value").stream().count();
System.out.println("stream count: " + count);
}
}5. The Comparison & Decision Layer
| Task | Old API | Modern API |
|---|---|---|
| Create list | new ArrayList<>() + add | List.of(...) |
| Check blank | trim().isEmpty() | isBlank() |
| Split lines | split("\\n") | lines() |
| Collect list | collect(Collectors.toList()) | toList() |
6. The "Interview Corner" (The Edge)
The "Killer" Interview Question: "List.of() vs Arrays.asList()?" Answer:
- List.of(): Truly immutable, no nulls, compact
- Arrays.asList(): Fixed-size, allows nulls, backed by array
java
List<String> asList = Arrays.asList("A", "B", "C");
asList.set(0, "X"); // ✅ OK: mutable elements
asList.add("D"); // ❌ ERROR: fixed size
List<String> listOf = List.of("A", "B", "C");
listOf.set(0, "X"); // ❌ ERROR: immutable
listOf.add("D"); // ❌ ERROR: immutable
Arrays.asList("A", null); // ✅ OK
List.of("A", null); // ❌ ERROR: no nullsPro-Tip: takeWhile vs filter:
java
List<Integer> numbers = List.of(1, 2, 3, 4, 1, 2);
// filter: checks ALL elements
numbers.stream()
.filter(n -> n < 4)
.toList(); // [1, 2, 3, 1, 2]
// takeWhile: stops at first false
numbers.stream()
.takeWhile(n -> n < 4)
.toList(); // [1, 2, 3] (stops at 4!)