Project Based Learning in Java

Wrapper Classes and Autoboxing

Primitive wrappers, autoboxing, unboxing, parsing, equality, and null handling.

Lecture 2.2.1 CO4 aligned Unit 2 · Chapter 2.2

Learning outcome: Match primitive types to wrapper classes, convert between values and objects, parse text input, and avoid common boxing and comparison errors.

Section 2.2 connects three tasks in a Java application: representing values as objects, moving data into and out of the program, and processing data with functions. Begin with wrappers, which explain why the collections studied in Chapter 2.1 use Integer instead of int.

The complete programs in this section use Java 8-compatible syntax and APIs. Save each program under the filename shown, compile it with javac Filename.java, and run it with java Filename from the same directory.

What Is a Wrapper Class?

A wrapper class represents a primitive value as an object. The eight classes below belong to java.lang, so no import is needed. Their objects are immutable: the stored value cannot be changed after construction.

Primitive Wrapper Example assignment
byte Byte Byte level = 10;
short Short Short count = 200;
int Integer Integer marks = 85;
long Long Long distance = 5000L;
float Float Float rate = 2.5F;
double Double Double average = 81.5;
char Character Character grade = 'A';
boolean Boolean Boolean passed = true;

The six numeric wrappers extend Number; Character and Boolean do not. Void is a separate type used to represent the absence of a result in some APIs; it is not one of these eight value wrappers.

Why Use Wrappers?

Generic type arguments must be reference types. A List<Integer> can therefore store integer values through boxing, whereas List<int> is invalid. Wrappers also provide parsing, comparison, and conversion methods. A wrapper reference can be null, which can represent a missing value; a primitive cannot.

In simple terms: A primitive is the value itself. A wrapper lets that value participate in APIs that work with objects. Use primitives for ordinary arithmetic and wrappers when an object or a missing-value representation is needed.

Boxing and Unboxing

Boxing converts a primitive to its wrapper type. Unboxing extracts the primitive. Java inserts these conversions automatically when a compatible assignment or method call requires them. See Oracle's autoboxing explanation.

Save as WrapperDemo.java:

import java.util.ArrayList;
import java.util.List;

public class WrapperDemo {
    public static void main(String[] args) {
        int raw = 84;
        Integer explicit = Integer.valueOf(raw);
        Integer automatic = raw;               // autoboxing
        int first = explicit.intValue();
        int second = automatic;                // unboxing

        List<Integer> marks = new ArrayList<>();
        marks.add(raw);                         // boxes int
        marks.add(90);
        int total = marks.get(0) + marks.get(1); // unboxes both

        System.out.println(first + " " + second);
        System.out.println(marks);
        System.out.println("Total: " + total);
    }
}

Output:

84 84
[84, 90]
Total: 174

Prefer autoboxing or valueOf to constructor expressions such as new Integer(84). Numeric wrapper constructors are deprecated in modern Java.

Parsing and Useful Methods

Text input is not automatically converted into a number. Choose a parser explicitly.

Expression Result Purpose
Integer.parseInt("42") primitive int value 42 Parse a decimal integer
Integer.valueOf("42") Integer representing 42 Parse into a wrapper
Double.parseDouble("8.5") primitive double value 8.5 Parse decimal text
Integer.toString(42) string "42" Convert a number to text
Integer.compare(8, 12) negative integer Compare without subtraction overflow
Character.isDigit('7') true Classify a character
Character.toUpperCase('a') 'A' Change character case
Boolean.parseBoolean("TRUE") true Parse a boolean string

Integer.parseInt throws NumberFormatException for invalid text or a value outside the int range. Trim surrounding spaces when appropriate, then validate the application's range separately. A successfully parsed mark of 150 is still invalid if marks must lie between 0 and 100.

Boolean.parseBoolean returns true only for text equal to "true", ignoring case; other text, including "yes" and null, gives false. Boolean.getBoolean reads a system property and is not a text parser. Similarly, Integer.getInteger reads a system property. These distinctions are documented in the Boolean API and Integer API.

Equality, Null, and Immutability

With two wrapper references, == compares object identity. Boxing can reuse cached objects, so identity comparisons can appear to work for some numbers. Compare numeric values with equals, Objects.equals for nullable references, or a deliberate primitive comparison. Integer.valueOf(5).equals(Long.valueOf(5)) is false because the wrapper types differ.

Unboxing null throws NullPointerException. This includes arithmetic such as missing + 1 and comparisons such as missing > 40. Check for missing data before using its value.

Java always passes arguments by value, including copies of object references. Wrapping an integer does not allow a method to replace the caller's value. Save as WrapperPitfalls.java:

import java.util.Objects;

public class WrapperPitfalls {
    static void addBonus(Integer mark) {
        mark = mark + 5; // reassigns only the local parameter
    }

    public static void main(String[] args) {
        Integer mark = 70;
        addBonus(mark);
        System.out.println("After call: " + mark);
        System.out.println(Objects.equals(mark, Integer.valueOf(70)));

        Integer missing = null;
        if (missing == null) {
            System.out.println("Mark not entered");
        }

        try {
            Integer.parseInt("8O"); // letter O, not zero
        } catch (NumberFormatException e) {
            System.out.println("Enter digits only");
        }
    }
}

Output:

After call: 70
true
Mark not entered
Enter digits only

To update the caller's variable, return the new value and assign it at the call site. Do not use wrapper objects as synchronization locks; use a dedicated lock object.

Common Mistakes

Mistake Correction
Declaring List<int> Use List<Integer>.
Treating a wrapper as a mutable numeric holder Return the changed value or design a separate mutable class.
Comparing wrapper references with == Use value comparison.
Unboxing a missing mark Handle null before arithmetic.
Calling marks.remove(1) to remove value 1 This removes index 1; use marks.remove(Integer.valueOf(1)) for the value.

Practice

  1. Store five marks in an ArrayList<Integer> and compute their average using primitive arithmetic.
  2. Convert the strings "75", " 90 ", "absent", and "150" into valid marks where possible. Report why each rejected value is invalid.
  3. Write a method that returns a mark after adding a bonus, and assign the return value to the original variable.

Quick Check

1. What is the difference between parseInt and valueOf(String)?

parseInt returns a primitive int; valueOf(String) returns an Integer.

2. Does mark++ mutate an Integer object?

No. It unboxes the value, increments it, and boxes the result before assigning the reference back to the variable.

3. Why can an expression involving Integer throw NullPointerException?

If the reference is null and Java needs a primitive value, unboxing fails.

Summary

Wrappers connect primitive values to object-based APIs. Keep conversion, parsing, and validation distinct, compare values deliberately, and handle null before unboxing. Continue with I/O streams and file handling.