🇸🇦 العربية
CPCS203  ·  Module 05  ·  Part 3 of 3

🎁 Wrapper Classes & Big Numbers

Wrapping Primitives · Auto-Boxing · BigInteger · BigDecimal

🎁 Wrapper Classes 🔄 Auto-Boxing 🔢 BigInteger 📐 BigDecimal
📋 Table of Contents
  1. Wrapper Classes — Wrapping Primitives as Objects
  2. BigInteger & BigDecimal — Computing Beyond Limits
1
Wrapper Classes — Wrapping Primitives as Objects
🔌 Real-World Story: The One Million Devices

Ahmad was a businessman. He travelled to the United States, found an amazing deal at a factory, and bought one million electronic devices — investing everything he had. The shipment arrived at the port of Jeddah. He opened the first box, plugged a device into the wall… nothing. A burning smell. 💨

The devices were built for 110V. Saudi Arabia runs on 220V. One million devices — completely incompatible. He could not sell a single one. His business was over before it started.

Then his friend Khalid called. "Ahmad — do not touch a single device. Build a small box: it takes 220V from the wall, converts it to 110V inside, and gives your device exactly what it needs. Place it in between." Ahmad placed the wrapper box in between — nothing changed on either side — and his business was saved. 🎉

Java has the same problem. Primitives (int, double, char…) work perfectly on their own, but Java's object-oriented world — ArrayList, generics, APIs — only accepts objects. Java's solution is exactly Khalid's box: a Wrapper class (Integer, Double, Character…) placed in between. Nothing on either side is modified.

In Java, there are 8 primitive data types: byte, short, int, long, float, double, char, boolean. Some Java APIs and data structures (like ArrayList) can only work with objects, not primitives. Solution? Java provides a wrapper class for each primitive that wraps it in an object!

Wrapper Class: Integer
Outside (Object world)
Integer object
42
private int value = 42;
Methods: intValue(), compareTo(), parseInt()
Primitive Type Wrapper Class Example
byteByteByte b = new Byte((byte)5);
shortShortShort s = new Short((short)100);
intIntegerInteger i = new Integer(42);
longLongLong l = new Long(100L);
floatFloatFloat f = new Float(3.14f);
doubleDoubleDouble d = new Double(2.718);
charCharacterCharacter c = new Character('A');
booleanBooleanBoolean b = new Boolean(true);
📄 WrapperDemo.java
public class WrapperDemo { public static void main(String[] args) { // Auto-boxing: primitive → wrapper object (Java does this automatically) Integer intObj = 42; // same as: new Integer(42) Double dblObj = 3.14; // same as: new Double(3.14) // Auto-unboxing: wrapper object → primitive (Java does this automatically) int x = intObj; // same as: intObj.intValue() double y = dblObj; // same as: dblObj.doubleValue() // Useful static methods int parsed = Integer.parseInt("123"); // String → int String asStr = Integer.toString(456); // int → String int maxVal = Integer.MAX_VALUE; // 2,147,483,647 int minVal = Integer.MIN_VALUE; // -2,147,483,648 System.out.println(parsed); // 123 System.out.println(maxVal); // 2147483647 } }

Java's wrapper classes are built-in — but you can also build your own wrapper class. This is a powerful idea: take any primitive, wrap it inside a class, and add whatever methods you need. For example, we can define a class called MyInt that wraps an int and provides extra functionality.

📄 MyInt.java — your own custom wrapper
public class MyInt { private int value; // the wrapped primitive public MyInt(int value) { this.value = value; } public int getValue() { return value; } public boolean isEven() { return value % 2 == 0; } public boolean isPositive() { return value > 0; } public String toBinary() { return Integer.toBinaryString(value); } @Override public String toString() { return "MyInt(" + value + ")"; } } // Usage MyInt n = new MyInt(42); System.out.println(n.getValue()); // 42 System.out.println(n.isEven()); // true System.out.println(n.isPositive()); // true System.out.println(n.toBinary()); // 101010 System.out.println(n); // MyInt(42)
💡 Why Wrapper Classes Exist

Some Java APIs (like ArrayList<Integer>, HashMap<String, Double>) only accept objects (they use generics). You cannot write ArrayList<int>. So wrapper classes let you store primitive values inside these structures.

Java's auto-boxing and auto-unboxing features (since Java 5) handle most conversions automatically, so you rarely need to call .intValue() manually.

✅ Quick Reference: Boxing & Unboxing
  • Auto-boxing: Java automatically converts a primitive to its wrapper type when needed. Integer x = 5; → Java writes new Integer(5) for you.
  • Auto-unboxing: Java automatically converts a wrapper object back to its primitive. int y = x; → Java calls x.intValue() for you.
  • Useful constants: Integer.MAX_VALUE, Integer.MIN_VALUE, Double.MAX_VALUE.
  • Parsing: Integer.parseInt("42") converts a String to an int — very common in real programs!
2
BigInteger & BigDecimal — Computing Beyond Limits

What if the number you need to compute is so large that it doesn't fit in a long (which maxes out at about 9.2 × 1018)? Or you need extremely precise decimal arithmetic — like in financial calculations or cryptography?

Java provides two special classes: BigInteger and BigDecimal in the java.math package.

🔢
BigInteger
Represents arbitrarily large integers — no overflow, ever. Supports all arithmetic operations: add(), subtract(), multiply(), divide(), pow(), mod().
📐
BigDecimal
Represents floating-point numbers with arbitrary precision — no rounding errors. Essential for financial and scientific computing where precision matters.
⚠️ Important Note

You cannot use +, -, *, / operators with BigInteger or BigDecimal. You must use their methods: .add(), .subtract(), .multiply(), .divide().

📄 BigNumberDemo.java
import java.math.BigInteger; import java.math.BigDecimal; public class BigNumberDemo { public static void main(String[] args) { // ── BigInteger ────────────────────────────────────────────────── BigInteger n1 = new BigInteger("99999999999999999999999999999"); BigInteger n2 = new BigInteger("11111111111111111111111111111"); System.out.println("n1 + n2 = " + n1.add(n2)); System.out.println("n1 * n2 = " + n1.multiply(n2)); System.out.println("n1 ^ 2 = " + n1.pow(2)); // ── BigDecimal ────────────────────────────────────────────────── BigDecimal price = new BigDecimal("19.99"); BigDecimal tax = new BigDecimal("0.15"); BigDecimal total = price.multiply(tax.add(BigDecimal.ONE)); System.out.println("Total with tax = " + total); // Why NOT to use double for money: System.out.println(1.0 - 0.9); // prints 0.09999999999999998 ❌ System.out.println( new BigDecimal("1.0").subtract(new BigDecimal("0.9")) ); // prints 0.1 ✅ } }
n1 + n2 = 111111111111111111111111111110
n1 * n2 = 1111111111111111111111111111088888888888888888889
n1 ^ 2 = 9999999999999999999999999999800000000000000000000000000001
Total with tax = 22.9885
double subtraction: 0.09999999999999998 ❌
BigDecimal subtraction: 0.1 ✅
🔐 Real-World Use: Cryptography!

A student asked: "Why would we ever need numbers this large?"

In cryptography, algorithms like RSA require working with integers that are hundreds of digits long (e.g., 2048-bit or 4096-bit numbers). The prime factorisation of such numbers is what makes encryption secure. BigInteger is the standard tool for implementing these algorithms in Java.

📌 Note for Students

BigInteger and BigDecimal are not strictly OOP concepts — they are Java library features. We discuss them here because they demonstrate how Java wraps complex numeric behaviour inside classes with methods, which is a great example of encapsulation and abstraction in action.

Module 05 Complete — Full Summary
🎉 Everything Covered in Module 05 (All Three Parts)
🔒
Mutable vs Immutable (Part 1)
Three rules for an immutable class: all fields private, no setters, no getter returning a mutable reference (return defensive copies instead).
🔧
Dependency — "uses" (Part 2)
Temporary usage of another class as a method parameter. Not part of the object's identity. UML: dashed arrow.
📦
Aggregation — "has-a", weak (Part 2)
The contained object can exist independently and may be shared. Received from outside via constructor parameter. UML: open diamond ◇.
💎
Composition — "owns-a", strong (Part 2)
The contained object is created inside the owner and cannot outlive it. Created inside constructor with new. UML: filled diamond ◆.
🔄
Self-Aggregation (Part 2)
A class that has a field of its own type (e.g., Student supervisor inside Student). Foundation for Linked Lists in Data Structures.
🔢
Multiplicity (Part 2)
Specifies how many objects participate in a relationship: 1, 0..1, *, 1..*, ranges like 5..60.
🎁
Wrapper Classes (Part 3)
Java wraps each of the 8 primitives in a class (e.g., int → Integer) to allow them to be used as objects. Auto-boxing/unboxing handles conversions automatically.
🔢
BigInteger & BigDecimal (Part 3)
Java library classes for arbitrarily large integers and high-precision decimals. Use methods (add(), multiply()) — not operators. Essential for cryptography and finance.