Modern Java Features
Stream API, lambdas, Optional, var, records — Java 8-21 features
Introduction
In this lesson, you'll learn about modern java features in Java. Coming from C, you already have a foundation for understanding this concept. We'll build on that knowledge while highlighting the key differences.
In C, you're familiar with stream api, lambdas, optional, var, records — java 8-21 features.
Java has its own approach to stream api, lambdas, optional, var, records — java 8-21 features, which we'll explore step by step.
The Java Way
Let's see how Java handles this concept. Here's a typical example:
import java.util.*;
import java.util.stream.*;
// Stream API + lambdas (Java 8+)
List<Integer> nums = List.of(1,2,3,4,5,6);
int sum = nums.stream()
.filter(x -> x % 2 == 0) // lambda replaces function pointer
.mapToInt(Integer::intValue) // method reference
.sum();
// sum = 12
// Collect to list
List<Integer> evens = nums.stream()
.filter(x -> x % 2 == 0)
.collect(Collectors.toList());
// Optional<T> — explicit nullable (vs C's -1 sentinel)
Optional<Integer> first = nums.stream()
.filter(x -> x > 3)
.findFirst(); // Optional[4]
first.ifPresent(v -> System.out.println("Found: " + v));
int val = first.orElse(-1); // -1 if absent
// var — local type inference (Java 10+)
var map = new HashMap<String, List<Integer>>();
// Records (Java 16+) — immutable data classes
record Point(double x, double y) {}
var p = new Point(1.0, 2.0);
System.out.println(p); // Point[x=1.0, y=2.0]
System.out.println(p.x()); // getter
// Text blocks (Java 15+)
String json = """
{"name": "Alice", "age": 30}
""";Comparing to C
Here's how you might have written similar code in C:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// Manual loop for filtering
int evens[100], count = 0;
int nums[] = {1,2,3,4,5,6};
int n = 6;
for (int i = 0; i < n; i++)
if (nums[i] % 2 == 0) evens[count++] = nums[i];
// Manual sum
int sum = 0;
for (int i = 0; i < count; i++) sum += evens[i];
// No lambdas — use function pointers
typedef int (*Predicate)(int);
int isEven(int x) { return x % 2 == 0; }
// Optional-like: return -1 for "not found"
int findFirst(int *arr, int n, Predicate pred) {
for (int i = 0; i < n; i++)
if (pred(arr[i])) return arr[i];
return -1; // "not found"
}You may be used to different syntax or behavior.
Stream API replaces manual for loops with declarative filter/map/reduce pipelines
You may be used to different syntax or behavior.
Lambdas (x -> expr) replace function pointers — more concise, type-safe
You may be used to different syntax or behavior.
Optional<T> replaces sentinel values (-1, null) for absent results
You may be used to different syntax or behavior.
var enables type inference for local variables — reduces verbosity
You may be used to different syntax or behavior.
Records replace data-only classes — auto-generate constructor, getters, equals, toString
Step-by-Step Breakdown
1. Streams and Lambdas
Streams are lazy pipelines on collections. Lambdas (x -> expr) are inline function definitions — cleaner than C's function pointers.
// C: manual loop with function pointer
for(int i=0;i<n;i++) if(pred(arr[i])) process(arr[i]);list.stream()
.filter(x -> x % 2 == 0) // lambda
.map(x -> x * x) // lambda
.forEach(System.out::println); // method ref2. Optional
Optional<T> makes the possibility of absence explicit in the API. Better than -1 sentinels or null returns.
// C: -1 or NULL means "not found"
int result = findFirst(arr, n, isEven);
if (result != -1) use(result);Optional<Integer> result = list.stream()
.filter(x -> x % 2 == 0)
.findFirst();
result.ifPresent(v -> use(v));
int v = result.orElse(0);3. Records
Records declare immutable data classes in one line. Auto-generated: compact constructor, accessors (name()), equals, hashCode, toString.
typedef struct { double x, y; } Point;
// No equals, toString, hashCoderecord Point(double x, double y) {}
var p = new Point(1.0, 2.0);
p.x(); // getter — no getX()
p.equals(new Point(1.0, 2.0)); // true
System.out.println(p); // Point[x=1.0, y=2.0]4. var and Text Blocks
var infers the type from the right-hand side. Text blocks (triple-quoted strings) replace messy string concatenation for multi-line strings.
// C: must always specify type
HashMap<String, Integer> map = new HashMap<>();
char *json = "{\"name\": \"Alice\"}"; // escaped messvar map = new HashMap<String, Integer>(); // inferred
String json = """
{"name": "Alice"}
"""; // no escaping neededCommon Mistakes
When coming from C, developers often make these mistakes:
- Stream API replaces manual for loops with declarative filter/map/reduce pipelines
- Lambdas (x -> expr) replace function pointers — more concise, type-safe
- Optional<T> replaces sentinel values (-1, null) for absent results
Key Takeaways
- Stream API: filter/map/reduce replace manual loops; lambdas replace function pointers
- Optional<T> replaces sentinel values (null, -1) for explicitly-absent results
- Records: one-line immutable data class with auto-generated constructor/getters/equals/toString
- var: local type inference; text blocks: multi-line strings without escaping