StudyToCert

OCP Java / Lecciones · English

Oracle Certified Professional: Java SE Developer 1Z0-831 (Java SE 25)

Clases envoltorio, autoboxing/unboxing y la caché de Integer con ==

Cada tipo primitivo tiene una clase envoltorio (wrapper) en java.lang: Byte, Short, Integer, Long, Float, Double, Character y Boolean. Los wrappers existen porque las colecciones y los genéricos solo funcionan con objetos, así que no puedes escribir List<int>. Un objeto wrapper es inmutable y también puede ser null, algo que un primitivo no puede ser.

El autoboxing es la conversión automática que hace el compilador de un primitivo a su wrapper, y el unboxing es lo contrario. Cuando escribes Integer n = 5; el compilador inserta Integer.valueOf(5), y cuando escribes int m = n; inserta n.intValue(). Por lo tanto, hacer unboxing de una referencia null lanza una NullPointerException en tiempo de ejecución, un escenario frecuente en el examen con Map.get devolviendo null a una variable int.

Conoce la diferencia entre las dos familias de conversión. Integer.parseInt("42") devuelve un int primitivo, mientras que Integer.valueOf("42") devuelve un objeto Integer. Los constructores de los wrappers como new Integer(5) están obsoletos (deprecated) y marcados para eliminación, así que el código moderno y las respuestas del examen usan valueOf o autoboxing. El boxing solo va al wrapper correspondiente: Long x = 5; no compila, porque 5 es un int y tendría que convertirse en un Integer, que no es un Long.

El operador == entre dos referencias wrapper compara identidad (si son el mismo objeto), no el valor. Integer.valueOf mantiene una caché de objetos Integer al menos para el rango -128 a 127, y el autoboxing usa valueOf, así que dos valores con boxing dentro de ese rango normalmente apuntan al mismo objeto en caché. Fuera de ese rango, cada boxing suele crear un objeto nuevo. Por eso Integer a = 127, b = 127; a == b es true pero la misma prueba con 128 es false. Short, Long y Byte guardan en caché el mismo rango, Character guarda de 0 a 127, y Boolean solo tiene TRUE y FALSE. La solución es sencilla: compara los valores de los wrappers con equals.

Mezclar un wrapper y un primitivo en == es distinto: se hace unboxing del wrapper, así que la comparación es numérica y la caché no importa. Cuidado también con equals entre tipos distintos. Long.valueOf(1).equals(1) es false, porque el argumento se convierte en un Integer, y Long.equals devuelve false para cualquier cosa que no sea un Long.

Integer a = 100, b = 100;
Integer c = 1000, d = 1000;
System.out.println(a == b);      // true  (en caché)
System.out.println(c == d);      // false (objetos distintos)
System.out.println(c.equals(d)); // true
int e = 1000;
System.out.println(c == e);      // true  (se hace unboxing de c)

La resolución de sobrecarga también involucra boxing. El compilador primero busca una coincidencia exacta o por ensanchamiento (widening), luego intenta boxing y unboxing, y solo después varargs. Así que, dados m(long) y m(Integer), la llamada m(5) elige m(long), porque el ensanchamiento gana sobre el boxing.

Términos clave

Wrapper class (clase envoltorio)
Una clase inmutable como Integer o Double que contiene un valor primitivo como objeto.
Autoboxing
La conversión automática de un primitivo a su wrapper, que el compilador realiza mediante valueOf.
Unboxing
La conversión automática de un wrapper a su primitivo; lanza NullPointerException si la referencia es null.
Integer cache (caché de Integer)
Un conjunto de objetos Integer, al menos de -128 a 127, que Integer.valueOf reutiliza, lo que hace que == parezca funcionar con valores pequeños.
Ejemplo real

Una tienda cuenta los pedidos por cliente en un Map<String, Integer>. El código int count = counts.get(id); funciona en pruebas, pero falla en producción con una NullPointerException la primera vez que aparece un cliente nuevo, porque get devuelve null y el unboxing falla. Usar getOrDefault(id, 0) lo evita.

Consejo para el examen: Cuando veas == entre dos variables Integer, revisa si ambos valores están entre -128 y 127. Si lo están, espera true; si no, espera false. Si un lado es primitivo, es una simple comparación numérica.

Comprueba lo que sabes

¿Qué imprime Integer x = 128, y = 128; System.out.println(x == y);?

Normalmente false. 128 está fuera del rango de caché garantizado, así que cada boxing crea un objeto distinto y == compara identidad.

¿Compila Long total = 10;?

No. 10 es un int, y el autoboxing solo produce un Integer, que no se puede asignar a Long. Escribe 10L.

¿Qué ocurre cuando se ejecuta Integer n = null; int m = n;?

Compila pero lanza una NullPointerException, porque el unboxing llama a intValue() sobre null.

Estudia OCP Java gratis
Plan semana a semana con lecciones, cuestionarios, simulaciones de examen y práctica, en español.
Abrir el plan de estudio