Java Programming · Lesson 13 of 15 · Updated ·
Java 8 Features — Lambdas, Method References, Streams and Optional
Java 8 changed how Java code looks. Lambdas and streams let you describe what you want from a collection instead of writing loops — and they come up in almost every Java interview.
1. Lambda expressions
A lambda is a short anonymous function: (parameters) -> expression. It implements a functional interface (one abstract method).
import java.util.*;
import java.util.function.*;
public class Main {
public static void main(String[] args) {
Function<Integer, Integer> square = x -> x * x;
Predicate<String> isLong = s -> s.length() > 4;
BiFunction<Integer, Integer, Integer> add = (a, b) -> a + b;
Supplier<String> hello = () -> "hello";
Consumer<String> shout = s -> System.out.println(s.toUpperCase());
System.out.println(square.apply(6) + " " + isLong.test("Java") + " " + add.apply(2, 3) + " " + hello.get());
shout.accept("lambdas");
}
}
36 false 5 hello
LAMBDAS
| Interface | Takes → gives | Method |
|---|---|---|
Function<T, R> | T → R | apply |
Predicate<T> | T → boolean | test |
Consumer<T> | T → nothing | accept |
Supplier<T> | nothing → T | get |
2. Method references
When a lambda just calls one method, write Class::method:
s -> s.toUpperCase()→String::toUpperCasex -> System.out.println(x)→System.out::println
3. Streams
A stream is a pipeline: a source → zero or more intermediate operations (filter, map, sorted, distinct, limit) → one terminal operation (collect, forEach, count, reduce, sum). Streams don't change the original collection, and nothing runs until the terminal operation (lazy).
import java.util.*;
import java.util.stream.*;
public class Main {
public static void main(String[] args) {
List<String> names = List.of("asha", "ravi", "neha", "kabir", "om");
List<String> result = names.stream()
.filter(n -> n.length() > 3)
.map(String::toUpperCase)
.sorted()
.collect(Collectors.toList());
System.out.println(result);
List<Integer> nums = List.of(4, 8, 15, 16, 23, 42);
int sumOfEvens = nums.stream().filter(n -> n % 2 == 0).mapToInt(Integer::intValue).sum();
long countBig = nums.stream().filter(n -> n > 10).count();
int product = nums.stream().limit(3).reduce(1, (a, b) -> a * b);
System.out.println(sumOfEvens + " " + countBig + " " + product);
}
}
[ASHA, KABIR, NEHA, RAVI]
70 4 480
Grouping and statistics
import java.util.*;
import java.util.stream.*;
record Student(String name, String city, int marks) {}
public class Main {
public static void main(String[] args) {
List<Student> list = List.of(
new Student("Asha", "Jaipur", 92), new Student("Ravi", "Pune", 74),
new Student("Neha", "Jaipur", 85), new Student("Om", "Pune", 66));
Map<String, List<String>> byCity = list.stream()
.collect(Collectors.groupingBy(Student::city, TreeMap::new,
Collectors.mapping(Student::name, Collectors.toList())));
System.out.println(byCity);
double avg = list.stream().mapToInt(Student::marks).average().orElse(0);
String topper = list.stream().max(Comparator.comparingInt(Student::marks)).map(Student::name).orElse("none");
System.out.println(avg + " " + topper);
}
}
{Jaipur=[Asha, Neha], Pune=[Ravi, Om]}
79.25 Asha
(record — Java 16+ — is a short way to write a class that just holds data.)
4. Optional: no more surprise nulls
Optional<T> is a box that may or may not contain a value. It forces you to think about the empty case.
import java.util.*;
public class Main {
public static void main(String[] args) {
Optional<String> found = List.of("java", "python").stream().filter(s -> s.startsWith("p")).findFirst();
Optional<String> missing = List.of("java").stream().filter(s -> s.startsWith("z")).findFirst();
System.out.println(found.orElse("none") + " " + missing.orElse("none") + " " + missing.isPresent());
System.out.println(found.map(String::length).orElse(0));
}
}
python none false
6
Common mistakes
- Reusing a stream after a terminal operation (
IllegalStateException) — create a new one. - Writing long, unreadable stream chains; a loop can be clearer.
- Calling
optional.get()without checking — useorElse,orElseThrow,map.
Quick recap
- Lambda:
(x) -> …, implements a functional interface (Function, Predicate, Consumer, Supplier). - Method references:
Class::method. - Stream = source → intermediate ops (lazy) → one terminal op.
groupingBy,mapToInt().sum()/average(),reduce,Optional.