This guide explains where and why wrapper classes are used in Java, and highlights the major features introduced in Java 8 with practical examples.
Wrapper classes are used whenever objects are required instead of primitives.
Collections cannot store primitives, only objects.
List<Integer> list = new ArrayList<>();
list.add(10); // autoboxing: int → Integer
int val = list.get(0); // unboxing: Integer → intGenerics only work with objects.
public class Box<T> {
T value;
}
Box<Integer> intBox = new Box<>(); // int wouldn’t workPrimitives can’t hold null, wrappers can.
Integer age = null; // allowed, unlike intBuilt-in parsing and conversion methods.
int n = Integer.parseInt("123");
double d = Double.parseDouble("3.14");
boolean b = Boolean.parseBoolean("true");Wrapper types are required in Stream API.
List<Integer> numbers = List.of(1, 2, 3);
numbers.stream().map(n -> n * 2).forEach(System.out::println);Wrappers implement Comparable.
List<Integer> nums = Arrays.asList(10, 5, 20);
Collections.sort(nums); // uses Integer.compareToFrameworks prefer wrappers.
public class User {
private Integer id;
private Boolean active;
}Wrappers in concurrent utilities.
AtomicInteger counter = new AtomicInteger(0);
counter.incrementAndGet();| Use Case | Why Wrapper is Used |
|---|---|
| Collections (List, Map) | Only accept objects |
| Generics | Only support objects |
| Nullability | Primitives can’t be null |
| Parsing | Static methods in wrapper classes |
| Lambda & Streams | Work with object types |
| Frameworks | Reflection, annotations |
| Multithreading | Thread-safe atomic wrappers |
Java 8 introduced functional programming and performance improvements.
// Before Java 8
Collections.sort(list, new Comparator<String>() {
public int compare(String a, String b) {
return a.compareTo(b);
}
});
// Java 8
list.sort((a, b) -> a.compareTo(b));Use Case: Sorting, event handling, iteration.
@FunctionalInterface
interface Calculator {
int operate(int a, int b);
}
Calculator add = (a, b) -> a + b;
System.out.println(add.operate(2, 3)); // 5Common ones: Predicate<T>, Function<T,R>, Consumer<T>, Supplier<T>
List<String> names = List.of("Ram", "Shyam", "Mohan");
names.stream()
.filter(name -> name.startsWith("S"))
.map(String::toUpperCase)
.forEach(System.out::println);Use Case: Efficient bulk operations.
names.forEach(System.out::println);Types of Method References:
- Static →
ClassName::staticMethod - Instance (specific object) →
object::method - Instance (arbitrary object) →
Class::method - Constructor →
Class::new
class Utils {
static void print(String s) { System.out.println(s); }
}
List<String> names = List.of("A", "B", "C");
names.forEach(Utils::print); // static referenceinterface Vehicle {
default void start() {
System.out.println("Starting...");
}
}Use Case: Backward compatibility.
Optional<String> name = Optional.ofNullable(getName());
name.ifPresent(System.out::println);Use Case: Avoid NullPointerException.
// Lambda
list.forEach(s -> System.out.println(s));
// Method Reference
list.forEach(System.out::println);✅ Method references are cleaner when reusing existing methods.
- Wrapper Classes → Bridge between primitives and objects. Essential for collections, generics, null handling, concurrency, and frameworks.
- Java 8 Features → Ushered functional programming in Java (Lambdas, Streams, Method References, Optional, Default Methods).
👉 Together, they enable modern, robust, and cleaner Java code.