Topic Note: Autoboxing, Unboxing & Enums¶
Course: Java Programming Masterclass - Tim Buchalka (Udemy)
Section: 10 - Mastering Lists, Iterators, and Autoboxing (Part 3: Autoboxing + Enums)
Status: Complete (Part 4 of Topic 3)
Learning Objectives¶
- Understand why wrapper classes exist and when they are needed
- Manually box primitives using
valueOf()and understand deprecatednew Integer()constructors - Leverage autoboxing (primitive → wrapper) and unboxing (wrapper → primitive) in variable assignments, method parameters, and return types
- Apply autoboxing/unboxing with arrays, varargs, and ArrayLists
- Build a simple Banking application using
ArrayList<Double>with autoboxing/unboxing - Declare and use
enumtypes with predefined constants - Use enum methods:
name(),ordinal(),values() - Switch on enum values in enhanced switch statements
- Add custom methods to an enum type
Part A: Autoboxing & Unboxing¶
1. Why Wrapper Classes Matter¶
Java has 8 primitive types (int, double, char, etc.) that are not objects. This causes a problem:
// ❌ WON'T COMPILE — generics don't accept primitives
LinkedList<int> numbers = new LinkedList<>();
// ✅ Must use the wrapper class instead
LinkedList<Integer> numbers = new LinkedList<>();
The Core Problem
Collections like ArrayList, LinkedList, and all generic types (List<T>, Map<K,V>, etc.) require objects, not primitives. You cannot write ArrayList<int> — only ArrayList<Integer>.
Primitive ↔ Wrapper Class Mapping¶
| Primitive | Wrapper Class | Size |
|---|---|---|
byte |
Byte |
8 bits |
short |
Short |
16 bits |
int |
Integer |
32 bits |
long |
Long |
64 bits |
float |
Float |
32 bits |
double |
Double |
64 bits |
char |
Character |
16 bits |
boolean |
Boolean |
1 bit |
Naming Convention
Most wrapper classes are simply the capitalized form of the primitive (double → Double). The exceptions are int → Integer and char → Character.
2. Manual Boxing & Unboxing¶
Boxing: Primitive → Wrapper¶
// Method 1: Factory method (preferred manual way)
Integer boxedInt = Integer.valueOf(15);
// Method 2: Constructor (DEPRECATED since JDK 9 — do NOT use)
Integer deprecatedBoxing = new Integer(15); // ⚠️ Marked for removal
Java's own documentation states:
"It is rarely appropriate to use this constructor. The static factory
valueOf(int)is generally a better choice, as it is likely to yield significantly better space and time performance."
The valueOf() method uses caching internally — for Integer, values between -128 and 127 are cached and reused, avoiding new object creation.
Unboxing: Wrapper → Primitive¶
Integer boxedValue = Integer.valueOf(15);
int unboxedInt = boxedValue.intValue(); // Manual unboxing
Double boxedDouble = Double.valueOf(3.14);
double unboxedDouble = boxedDouble.doubleValue(); // Manual unboxing
Each wrapper class has a corresponding xxxValue() method: intValue(), doubleValue(), charValue(), booleanValue(), etc.
You Almost Never Need Manual Boxing/Unboxing
IntelliJ will flag both Integer.valueOf() and .intValue() as unnecessary boxing/unboxing. Java handles this automatically — which brings us to autoboxing.
3. Autoboxing & Unboxing — Let Java Do the Work¶
Autoboxing (Primitive → Wrapper, Automatically)¶
Integer autoBoxed = 15; // Java auto-boxes int → Integer
Double autoBoxedDouble = 3.14; // Java auto-boxes double → Double
Underneath, Java is calling Integer.valueOf(15) for you, choosing the most efficient mechanism.
Auto-Unboxing (Wrapper → Primitive, Automatically)¶
flowchart LR
subgraph autoboxing["Autoboxing"]
P1["int 15"] -->|"auto-boxes"| W1["Integer(15)"]
end
subgraph unboxing["Unboxing"]
W2["Integer(15)"] -->|"auto-unboxes"| P2["int 15"]
end
style autoboxing fill:#2196F3,color:#fff
style unboxing fill:#FF9800,color:#fff
Important Limitation¶
Integer autoBoxed = 15;
System.out.println(autoBoxed.getClass().getName()); // ✅ java.lang.Integer
int autoUnboxed = autoBoxed;
// System.out.println(autoUnboxed.getClass().getName()); // ❌ COMPILER ERROR!
You cannot call methods on a primitive variable. getClass() is an inherited method from Object, and primitives are not objects.
4. Autoboxing in Method Parameters & Return Types¶
Autoboxing works seamlessly across method boundaries:
Return Type Autoboxing¶
private static Double getDoubleObject() {
return Double.valueOf(100.00); // Returns a wrapper object
}
private static double getLiteralDoublePrimitive() {
return 100.0; // Returns a primitive
}
// Auto-boxes: method returns a primitive, but assigned to wrapper
Double resultBoxed = getLiteralDoublePrimitive();
// Auto-unboxes: method returns a wrapper, but assigned to primitive
double resultUnboxed = getDoubleObject();
Parameter Type Autoboxing¶
private static int returnAnInt(Integer i) {
return i; // Unboxes Integer → int for the return type
}
private static Integer returnAnInteger(int i) {
return i; // Autoboxes int → Integer for the return type
}
The JVM handles all conversions transparently:
| Scenario | What Happens |
|---|---|
| Assign primitive to wrapper variable | Autoboxing |
| Assign wrapper to primitive variable | Unboxing |
| Pass primitive to method expecting wrapper parameter | Autoboxing |
| Pass wrapper to method expecting primitive parameter | Unboxing |
| Return primitive from method with wrapper return type | Autoboxing |
| Return wrapper from method with primitive return type | Unboxing |
5. Autoboxing with Arrays, Varargs & Lists¶
Wrapper Arrays¶
// Array of Integer wrappers (not int primitives)
Integer[] wrapperArray = new Integer[5];
wrapperArray[0] = 50; // Autoboxes int → Integer
System.out.println(Arrays.toString(wrapperArray));
// [50, null, null, null, null]
System.out.println(wrapperArray[0].getClass().getName());
// java.lang.Integer
Default Values Differ
-
int[]elements default to0 -
Integer[]elements default tonull(they are object references)
Array Initializer with Autoboxing¶
Character[] charArray = {'a', 'b', 'c'}; // Autoboxes char → Character
System.out.println(Arrays.toString(charArray));
// [a, b, c]
Varargs with Autoboxing¶
private static ArrayList<Integer> getList(int... varargs) {
ArrayList<Integer> alist = new ArrayList<>();
for (int i : varargs) {
alist.add(i); // Autoboxes each int → Integer
}
return alist;
}
Calling this method with primitive literals:
The add() method on ArrayList<Integer> expects an Integer parameter, but accepts int primitives through autoboxing.
Varargs Type Doesn't Matter
-
You can change the parameter type from
int... varargstoInteger... varargs -
the code still works identically. The JVM auto-boxes the literal
intvalues passed to the method before they're added to the varargs array.
Using List.of() with Primitives¶
This is the simplest way to create an immutable list of integers. Each literal is auto-boxed to an Integer instance.
6. Banking Challenge — Autoboxing in Practice¶
This challenge demonstrates autoboxing and unboxing in a real-world scenario: a simple banking application.
The Customer Record¶
record Customer(String name, ArrayList<Double> transactions) {
public Customer(String name, double initialDeposit) {
this(name.toUpperCase(),
new ArrayList<Double>(500)); // Capacity hint: ~500 transactions/year
transactions.add(initialDeposit); // ← AUTOBOXING: double → Double
}
}
Key points:
- The canonical constructor takes a
Stringand anArrayList<Double> - The custom constructor takes a primitive
double— autoboxing occurs whenadd()is called name.toUpperCase()normalizes customer names for case-insensitive matching- Initial capacity
500prevents incremental reallocations for typical usage
The Bank Class¶
class Bank {
private String name;
private ArrayList<Customer> customers = new ArrayList<>(5000);
public Bank(String name) {
this.name = name;
}
Encapsulation Pattern
The customers list has no getter. External code cannot access or modify the customer list directly — all operations go through Bank's public methods. This is a key OOP design principle.
Finding a Customer¶
private Customer getCustomer(String customerName) {
for (var customer : customers) {
if (customer.name().equalsIgnoreCase(customerName))
return customer;
}
System.out.printf("Customer (%s) wasn't found %n", customerName);
return null;
}
private— only the Bank can look up customers- Uses case-insensitive comparison (
equalsIgnoreCase) - Returns
nullif no match (simple error handling for this challenge)
Adding a New Customer (with Duplicate Prevention)¶
public void addNewCustomer(String customerName, double initialDeposit) {
if (getCustomer(customerName) == null) {
Customer customer = new Customer(customerName, initialDeposit);
customers.add(customer);
System.out.println("New Customer added: " + customer);
}
}
Note: getCustomer prints "not found" when checking for duplicates during addNewCustomer. In production code, you'd separate the "exists" check from the "not found" message.
Adding a Transaction (Autoboxing)¶
public void addTransaction(String name, double transactionAmount) {
Customer customer = getCustomer(name);
if (customer != null) {
customer.transactions().add(transactionAmount); // ← AUTOBOXING
}
}
transactionAmount is a primitive double → the add() method on ArrayList<Double> autoboxes it to a Double wrapper object.
Printing a Statement (Unboxing)¶
public void printStatement(String customerName) {
Customer customer = getCustomer(customerName);
if (customer == null) {
return;
}
System.out.println("_".repeat(30));
System.out.println("Customer Name: " + customer.name());
System.out.println("Transactions: ");
for (double d : customer.transactions()) { // ← UNBOXING
System.out.printf("$%10.2f (%s)%n",
d, d < 0 ? "Debit" : "Credit");
}
}
This is the unboxing part: The for-each loop iterates over ArrayList<Double> (wrapper objects), but the loop variable d is declared as a primitive double. Each Double wrapper object is automatically unboxed to double.
Usage & Output¶
Bank bank = new Bank("Chase");
bank.addNewCustomer("Jane A", 1000);
bank.addTransaction("Jane A", -10.25); // Debit
bank.addTransaction("Jane A", -75.01); // Debit
bank.printStatement("Jane A");
bank.addNewCustomer("Bob S", 25);
bank.addTransaction("Bob S", 100); // Credit
bank.printStatement("Bob S");
______________________________
Customer Name: JANE A
Transactions:
$ 1000.00 (Credit)
$ -10.25 (Debit)
$ -75.01 (Debit)
______________________________
Customer Name: BOB S
Transactions:
$ 25.00 (Credit)
$ 100.00 (Credit)
Where Autoboxing/Unboxing Occurs¶
flowchart TD
subgraph autoboxing_points["Autoboxing Points (double → Double)"]
AB1["Customer constructor:<br/>transactions.add(initialDeposit)"]
AB2["Bank.addTransaction:<br/>customer.transactions().add(transactionAmount)"]
end
subgraph unboxing_points["Unboxing Points (Double → double)"]
UB1["Bank.printStatement:<br/>for (double d : customer.transactions())"]
end
style autoboxing_points fill:#2196F3,color:#fff
style unboxing_points fill:#FF9800,color:#fff
Part B: Enums¶
7. What Is an Enum?¶
An enum (short for enumeration) is a special Java type that contains a fixed set of predefined constants.
Wikipedia: "A complete ordered listing of all the items in a collection."
Java: "A special data type that contains predefined constants."
An enum is like an array, except:
- Its elements are known at compile time and cannot change
- Each element is referred to by a named constant, not an index
- The keyword
enumreplacesclass
Declaring an Enum¶
Conventions:
- Enum name: UpperCamelCase (like a class)
- Constants: ALL_UPPERCASE (like final static fields)
- No semicolon needed after the last constant (unless you add methods/fields)
Common Enum Examples¶
enum Month { JANUARY, FEBRUARY, MARCH, /* ... */ DECEMBER }
enum Direction { NORTH, EAST, SOUTH, WEST }
enum Size { EXTRA_SMALL, SMALL, MEDIUM, LARGE, EXTRA_LARGE }
Enums Are Classes Underneath
Each enum constant is actually an instance of the enum class. Enums can have fields, methods, and constructors — they are much more than simple constant lists.
8. Using Enums in Code¶
Declaring and Assigning¶
Built-in Enum Methods¶
| Method | Returns | Description |
|---|---|---|
name() |
String |
The constant's name as declared (e.g., "TUE") |
ordinal() |
int |
Zero-based position in the enum (e.g., TUE → 2) |
values() |
EnumType[] |
Array of all constants in declaration order |
System.out.printf("Name is %s, Ordinal Value = %d%n",
weekDay.name(), weekDay.ordinal());
// Name is TUE, Ordinal Value = 2
Ordinal is Zero-Based
Like arrays and lists, enum ordinals start at 0. SUN is ordinal 0, SAT is ordinal 6. If you want 1-based numbering (e.g., "Day 1"), add 1: weekDay.ordinal() + 1.
Comparing Enums¶
Use == (not .equals()) for enum comparison — enum constants are singletons, so reference equality works perfectly and is preferred.
Iterating Over All Constants with values()¶
for (DayOfTheWeek day : DayOfTheWeek.values()) {
System.out.println(day.name() + " (ordinal: " + day.ordinal() + ")");
}
The values() method returns a new array of all enum constants in declaration order. You can use this array with enhanced for, indexed access, or Random:
Random Enum Value¶
public static DayOfTheWeek getRandomDay() {
int randomInteger = new Random().nextInt(7); // 0–6
var allDays = DayOfTheWeek.values(); // Returns DayOfTheWeek[]
return allDays[randomInteger];
}
Because values() returns a regular Java array, you can index into it with [].
9. Enums in Switch Statements¶
Enums work as switch expressions — this is one of their most powerful features:
public static void switchDayOfWeek(DayOfTheWeek weekDay) {
int weekDayInteger = weekDay.ordinal() + 1;
switch (weekDay) {
case WED -> System.out.println("Wednesday is day " + weekDayInteger);
case SAT -> System.out.println("Saturday is day " + weekDayInteger);
default -> System.out.println(
weekDay.name().charAt(0) +
weekDay.name().substring(1).toLowerCase() +
" is day " + weekDayInteger);
}
}
Key observations:
- The switch expression is the enum variable itself (
weekDay) - Case labels use the constant name without the enum type prefix — just
WED, notDayOfTheWeek.WED - The
defaultbranch uses String manipulation to convert"TUE"→"Tue"→"Tuesday is day 3"
Switch Expression Types
You can switch on: int, char, byte, short, String, and enum. Enum switches are especially readable because the case labels are human-readable constant names.
10. Adding Custom Methods to Enums¶
Since enums are classes, you can add methods. You must add a semicolon after the last constant before adding any members:
The Topping Enum with getPrice() Method¶
public enum Topping {
MUSTARD,
PICKLES,
BACON,
CHEDDAR,
TOMATO; // ← SEMICOLON required before methods
public double getPrice() {
return switch (this) { // Switch on the enum instance itself
case MUSTARD, PICKLES -> 0.50;
case BACON -> 1.50;
case CHEDDAR -> 1.00;
case TOMATO -> 0.75;
};
}
}
Key points:
- The
switch (this)expression refers to the current enum instance calling the method - Enhanced switch with
->returns different values per constant - Multiple case labels can share a result:
case MUSTARD, PICKLES -> 0.50 - No
defaultneeded — enum switch covers all possible values (compiler verifies exhaustiveness)
Using the Custom Method¶
for (Topping topping : Topping.values()) {
System.out.println(topping.name() + " : " + topping.getPrice());
}
Alternative: Enum with Fields and Constructors (Preview)¶
Another approach (covered in depth later in the course) uses a field and constructor:
public enum Topping {
MUSTARD(0.50),
PICKLES(0.50),
BACON(1.50),
CHEDDAR(1.00),
TOMATO(0.75);
private final double price;
Topping(double price) {
this.price = price;
}
public double getPrice() {
return price;
}
}
This pattern is more scalable when enums have multiple associated values.
Common Pitfalls Summary¶
| Pitfall | Example | Fix |
|---|---|---|
| Using primitive in generic type | ArrayList<int> |
Use wrapper: ArrayList<Integer> |
| Using deprecated wrapper constructors | new Integer(15) |
Use Integer.valueOf(15) or just 15 |
| Calling methods on primitives | autoUnboxed.getClass() |
Use the wrapper variable instead |
Unboxing null → NullPointerException |
Integer x = null; int y = x; |
Check for null before unboxing |
| Forgetting semicolon in enum | Adding methods without ; after constants |
Add ; after last constant before methods |
| Using enum type prefix in switch case | case DayOfTheWeek.WED: |
Just use case WED: |
| Assuming ordinal is 1-based | weekDay.ordinal() for "Day 1" |
Add 1: weekDay.ordinal() + 1 |
The Most Dangerous Pitfall: Unboxing null
- When a wrapper variable is
nulland Java tries to unbox it, you get aNullPointerException. This compiles without issues - the error only appears at runtime. Always check for
nullbefore unboxing, especially with values from collections or method returns.
Key Takeaways¶
Autoboxing & Unboxing¶
- Collections require objects, not primitives — this is the fundamental reason wrapper classes exist.
- Autoboxing (
int→Integer) and unboxing (Integer→int) happen automatically in assignments, method parameters, return types, and enhanced loops. - Prefer autoboxing over manual
valueOf()or deprecated constructors — let Java choose the most efficient mechanism. - Wrapper arrays default to
null, not0— a common source ofNullPointerException. - Performance consideration: Excessive boxing/unboxing in tight loops can cause unnecessary object creation. Use primitive arrays when performance matters significantly.
Enums¶
- Enums are fixed, ordered sets of named constants — use them instead of
intorStringconstants for type safety. - Enum constants are singletons — compare with
==, not.equals(). values()returns an array — iterate with enhancedforor index into it for random access.- Enums work in switch statements — case labels use the constant name without type prefix.
- Enums can have methods, fields, and constructors — they are full-featured classes with a fixed number of instances.
Quick Reference¶
Wrapper Class Methods¶
| Method | Description | Example |
|---|---|---|
Type.valueOf(primitive) |
Manual boxing (factory) | Integer.valueOf(15) |
wrapper.typeValue() |
Manual unboxing | myInt.intValue() |
Type.parseType(string) |
Parse string to primitive | Integer.parseInt("42") |
Type.toString(primitive) |
Convert to String | Integer.toString(42) |
Type.MAX_VALUE |
Maximum value for type | Integer.MAX_VALUE |
Type.MIN_VALUE |
Minimum value for type | Double.MIN_VALUE |
Enum Methods¶
| Method | Returns | Description |
|---|---|---|
name() |
String |
Constant name exactly as declared |
ordinal() |
int |
Zero-based position in the enum |
values() |
EnumType[] |
Array of all constants (static) |
valueOf(String) |
EnumType |
Look up constant by name (throws IllegalArgumentException) |
compareTo(other) |
int |
Compare ordinal positions |
toString() |
String |
Same as name() by default, can be overridden |
Related Notes¶
| Part | Topic | Link |
|---|---|---|
| 1 | Arrays & java.util.Arrays |
← Part 1 |
| 2 | ArrayList — Java's Resizable Array | ← Part 2 |
| 3 | LinkedList & Iterators | ← Part 3 |
| 4 | Autoboxing, Unboxing & Enums | You are here |
References¶
- Course: Tim Buchalka - Java Programming Masterclass (Section 10, Lectures 15–18, 20–21)
- API: java.lang.Integer (Java 17)
- API: java.lang.Enum (Java 17)
- Guide: Autoboxing and Unboxing (Oracle Tutorial)
- Guide: Enum Types (Oracle Tutorial)
Last Updated: 2026-02-11 | Confidence: 9/10