1. Introducción
En el lenguaje Java existen dos familias de datos diferentes. La primera son los tipos primitivos: int, char, double, boolean y otros. Son simples y rápidos: el valor se almacena directamente en la celda de memoria. La segunda familia son los tipos por referencia, es decir, los objetos: la variable almacena una referencia al lugar donde el objeto está ubicado en memoria.
A veces necesitas utilizar un primitivo como un objeto. Pero los primitivos no son objetos por naturaleza. En esos casos ayudan las clases envoltorio.
Los tipos envoltorio son clases que guardan en su interior el valor de un primitivo y permiten tratarlo como un objeto. Por ejemplo, para int — la clase Integer, para double — Double, para char — Character, para boolean — Boolean. Estas clases proporcionan métodos como: Integer.parseInt(), Double.isInfinite(), Character.isLetter(), Boolean.parseBoolean(), etc.
Ejemplo de obtención de un objeto Integer a partir de un primitivo int:
// Primitivo
int a = 10;
// Objeto envoltorio
Integer b = Integer.valueOf(10);
En la variable a se almacena el número 10 en sí. Y la variable b almacena una referencia a un objeto que contiene en su interior el 10.
2. Para qué sirven los envoltorios
Un primitivo es siempre un valor concreto; no se le puede asignar null. Un objeto puede no existir y almacenar un valor vacío, lo cual es útil cuando se necesita indicar «desconocido» o «ausente».
Un primitivo no tiene métodos. No puedes invocar en un número algo como x.toString(). El envoltorio ofrece esas posibilidades: Integer.parseInt("123") convierte una cadena en número, y un número se puede convertir en cadena con toString().
En resumen: los envoltorios se necesitan donde hace falta un objeto — posibilidad de almacenar null, invocar métodos, y pasar valores a un API que solo acepta objetos (por ejemplo, colecciones como List<Integer>).
Lista completa de clases envoltorio
| Tipo primitivo | Clase envoltorio |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3. Boxing y unboxing
Cuando creas manualmente un objeto envoltorio, tomas un primitivo y lo «empaquetas» dentro de un objeto:
int primitive = 42;
Integer wrapper = Integer.valueOf(primitive); // empaquetado
Para volver a obtener el valor primitivo — «desempaquetar» — se usa, por ejemplo, intValue():
Integer wrapper = Integer.valueOf(42);
int primitive = wrapper.intValue(); // desempaquetado
Autoboxing (autoempaquetado)
El compilador añadirá automáticamente la llamada a valueOf() si asignas un primitivo a una variable envoltorio:
int a = 10;
Integer b = a; // autoboxing (Integer.valueOf(a))
Unboxing (autodesempaquetado)
La situación inversa — cuando un objeto envoltorio se usa donde se necesita un primitivo:
Integer c = 20;
int d = c; // unboxing (c.intValue())
int a = 10; // primitivo
Integer b = a; // autoboxing
Integer c = Integer.valueOf(20);
int d = c; // unboxing
4. Escollos
Comparación de envoltorios. El operador == para objetos compara referencias, no valores:
Integer m = 200;
Integer n = 200;
System.out.println(m == n); // false, porque son objetos distintos
Java mantiene en caché los envoltorios de números en el rango de -128 a 127, por eso a veces == devolverá true y otras — false. Para comparar valores, usa siempre equals():
Integer x = 100;
Integer y = 100;
System.out.println(x == y); // true, entra en la caché
System.out.println(x.equals(y)); // true, comparación por valor
NullPointerException durante el unboxing. Si el envoltorio es null y tratas de desempaquetarlo, obtendrás una excepción:
Integer value = null;
int primitive = value; // ¡Error! NullPointerException
Rendimiento y memoria. Los envoltorios son más lentos que los primitivos y ocupan más memoria. En zonas calientes del código y al trabajar con grandes arrays de números, elige primitivos.
5. Ejemplos
Ejemplos con Integer y Double
Conversión de cadena a número:
String text = "123";
int number = Integer.parseInt(text);
System.out.println(number); // 123
Comprobación de valores especiales de los números en coma flotante:
double d = 1.0 / 0;
System.out.println(Double.isInfinite(d)); // true
double nan = 0.0 / 0.0;
System.out.println(Double.isNaN(nan)); // true
Ejemplos con Character
char almacena un solo carácter; el envoltorio Character ofrece comprobaciones útiles:
char ch = 'A';
Character wrapper = ch; // autoboxing
System.out.println(Character.isLetter(ch)); // true
System.out.println(Character.isDigit(ch)); // false
System.out.println(Character.toLowerCase(ch)); // 'a'
Ejemplos con Boolean
El primitivo boolean solo puede ser true/false. El envoltorio Boolean permite almacenar también null:
Boolean flag = null; // permitido
flag = Boolean.TRUE; // constante especial
System.out.println(flag); // true
Conversión de cadenas a valores booleanos:
String s1 = "true";
String s2 = "false";
boolean b1 = Boolean.parseBoolean(s1);
boolean b2 = Boolean.parseBoolean(s2);
System.out.println(b1); // true
System.out.println(b2); // false
GO TO FULL VERSION