Wrapper Classes in Java
Autoboxing automatically wraps a primitive value in its corresponding wrapper class object when an object is required.
A Plain Number Where an Object Is Expected
Most Java code that stores numbers uses primitive types such as int, which hold a plain numeric value. Java also has wrapper classes such as Integer, which place that same number inside an object. Some parts of Java need an object rather than a primitive, and a common beginner question is how a plain int ends up in such a place without any visible conversion.
What do you think happens?
In the statement Integer answer = 42; the variable answer is declared as an Integer, but 42 is a plain int literal. What must the compiler do to make this line valid?
Reveal answer
The compiler inserts a call to Integer.valueOf(42), which returns an Integer object wrapping the value 42, and that object is what the variable receives.
The left side needs an object and the right side supplies a primitive. The compiler closes that gap by inserting the conversion itself, so the line behaves as if it had been written with an explicit Integer.valueOf(42) call.
Tracing What the Source Line Shows
- Integer is the declared type: the variable must hold an object
- 42 is an int literal: a primitive value written directly in the source
Read this line the way a learner would, and the visible text contains only two pieces: a type that expects an object and a number that is not an object. Nothing in the line shows a conversion step. This is the first trace point to notice. The source text tells you what you asked for, but the step that makes the line valid is not written anywhere you can see. That gap is why a trace that starts only from your source text can miss the exact moment a primitive changed form.
What Autoboxing Means
Autoboxing is the automatic conversion of a primitive type into its corresponding wrapper class object. The word boxing describes wrapping a value in an object. When the code needs an object, a primitive value is placed inside the matching wrapper: an int goes into an Integer.
You do not write the wrapping yourself. The compiler inserts it wherever the context requires an object. The benefit is convenience. Wrapper classes are useful, but without autoboxing you would have to wrap every value by hand each time an object was needed. Autoboxing removes that routine work, so a primitive can be used in a context that asks for an object without an explicit conversion in your source.
Autoboxing is decided by context, not by a keyword you type. The conversion happens where a primitive meets a place that requires an object.
The Call the Compiler Inserts
For an int, the method the compiler relies on is Integer.valueOf(). It takes a primitive int and returns an Integer object that wraps it. Because the compiler inserts this call silently, it does not appear in the source you wrote, and it is easy to forget it is there.
- The first form is the short form you typed
- The second form is the explicit form the compiler treats as equivalent
Read the two forms side by side. The short form is what you typed. The explicit form is what the compiler works with. Both produce an Integer object that holds the value 42, and the only difference is that the call in the second form is visible.
The diagram reads as a sequence of hand-offs. Your source statement creates the need for an object. The compiler supplies the call. The method wraps the primitive, and the resulting Integer object is what the variable receives. The only piece you never typed is the call in the middle.
Autoboxing Versus Unboxing
Autoboxing and unboxing are two directions of the same family of conversions. Autoboxing moves a value from a primitive into its wrapper object. Unboxing is the reverse, moving a value from a wrapper object back to a primitive. The source material gives full treatment to autoboxing and only names unboxing as a related idea, so the description here is limited to that direction.
| Feature | Autoboxing | Unboxing |
|---|---|---|
| Direction | Primitive to wrapper object | Wrapper object back to primitive |
| Example | int to Integer | Integer back to int |
| Who performs it | The compiler, where an object is required | Conversion the language performs for you |
| Depth in source material | Full treatment | Named briefly as a related idea |
Autoboxing and unboxing are opposite directions of one family of conversions.
Locating the Boxing Point in a Line
When a program produces an unexpected result involving wrapper types, the useful question is where the primitive became an object, or back again. The conversion can only happen where a primitive meets a context that requires an object, so those are the places to inspect first. The following example is a generated illustration of that idea, using a list that stores objects.
7- list is declared to hold Integer objects, so it needs objects, not primitives
- count is a plain int variable
- The call
list.add(count)is the point where the int meets an object-typed context
Tracing the list.add(count) Line
A program adds an int variable to a list of Integer and later reads the value back. Where does the primitive become an object?
- 1Identify the declared type of the target
The list is declared as List of Integer, so each element it receives must be an object.
- 2Identify the type of the value being passed
The variable count is declared as int, so it is a primitive value.
- 3Find where the two meet
The argument count is passed to add, a context that requires an object. This is the point where the conversion occurs.
- 4Trace the hand-off
The compiler inserts the wrapping call, the Integer object is created, and that object is stored in the list.
The primitive became an object at the argument position of list.add(count), not at the declaration of count or the call to println.
Mistakes When Tracing Wrapper Values
Common mistake Assuming the conversion must be written in the source
What it looks likeInteger answer = 42; looks like it has no conversion step
Why it failsAutoboxing is performed by the compiler wherever an object is required, so the wrapping call is inserted without appearing in your text.
Do this insteadAsk at each assignment or method argument whether the expected type is a primitive or an object.
Common mistake Looking for the conversion in the wrong place
What it looks likeSearching only the declaration of count when the surprise happens inside list.add(count)
Why it failsThe conversion happens where a primitive meets an object-typed context, which may be a later line than the variable's declaration.
Do this insteadStart from the line where the unexpected result appears and move backward to the nearest object-requiring context.
Common mistake Treating autoboxing and unboxing as the same direction
Why it failsAutoboxing goes from primitive to wrapper, and unboxing goes from wrapper back to primitive.
Do this insteadName the direction first, then identify which type sits on each side of the conversion.
Common mistake Forgetting that the inserted call is Integer.valueOf() for an int, which is why the explicit form is the useful mental model
Practice Tracing Conversions
Take the following three lines. For each line, state whether a primitive becomes an object there, and name the direction of any conversion: Integer a = 10; List<Integer> nums = new ArrayList<>(); nums.add(a + 1);
Hints
- Ask whether the declared type on the left, or the parameter type on the right, requires an object
- Check whether the value passed in is a plain int or already a wrapper object
- In the third line, the expression a + 1 must be evaluated before it is passed to add
Key Takeaways
- Autoboxing automatically wraps a primitive value in its corresponding wrapper class object when the context requires an object, so you do not write the wrapping yourself.
- For an int, the compiler silently uses
Integer.valueOf(), so Integer answer = 42; behaves as Integer answer =Integer.valueOf(42);. - Unboxing is the reverse direction, moving a value from a wrapper object back to a primitive; the source material treats it only briefly.
- When a result looks wrong, trace the line in order and find the place where a primitive meets an object-typed context, because that is where the conversion occurs.