Boxing, unboxing y autoboxing en Java

Tiempo estimado: 15 min.

Cada tipo en Java es un tipo de referencia o un tipo primitivo. Los primeros incluyen a las clases, interfaces y tipos de arreglo. Por otro lado existen ocho tipos primitivos: byte, char, short, int, float, double, long y boolean. Cada uno tiene una clase de librería con su tipo de referencia : Byte, Character, Short, Integer, Double,Long y Boolean. Se puede pensar en ellos como “adaptadores” que nos permiten usar los tipos primitivos para las operaciones que se mencionaron antes -las que no aceptan tipos primitivos.

A la conversión de un tipo primitivo a su tipo de referencia correspondiente se le llama Boxing, y a la operación inversa Unboxing. Es un concepto aparentemente sencillo, pero es extremadamente importante y además tiene implicaciones muy sutiles que pueden generar complicaciones .

Un aspecto muy notable de Java es que realiza las conversiones de forma automática cuando resulta apropiado, a esto se le llama Autoboxing, que a estas alturas resulta una obviedad.

Retomando el asunto de las sutilezas, está el operador “==” que si se usa con un tipo primitivo evalúa la igualdad de sus valores, pero aplicado a su tipo de referencia correspondiente evalúa la identidad de objeto. Para comprobar esto solo hay que ejecutar el siguiente código: 

int primero = 1;
int segundo = 1;
System.out.println( ” Primero igual a segundo? ”      +                                     ( primero == segundo ) );    //true
Integer prim = new Integer( 1 );
Integer seg = new Integer( 1 );
System.out.println(“prim igual a seg?” + ( prim == seg ) );     //false

Otra sutileza importante es el cacheo de objetos -a veces, para ahorrar memoria ,se usa la misma referencia para tipos de referencia con el mismo valor. Por lo tanto, en ocasiones el operador == si devolverá true para primitivos con el mismo valor. Como muestra, el siguiente código imprime true al comparar el primer elemento de la lista con todos los demas: 

ArrayList<Integer> lista = new ArrayList();

   for (int i = 0; i < 10; i++) {
      lista.add(1);
}

lista.get(0);
for (Integer comp : lista) {
    System.out.println( “elemento = comparando?”          +                                        ( elem  ==  comp ) );
}

Un último comentario con respecto al rendimiento: es preferible usar los tipos primitivos siempre que se pueda y solo usar sus tipos de referencia cuando sea estrictamente necesario. Así se evita la creación de instancias cuando no son necesarias, ahorrando tiempo de procesamiento y memoria. Consideremos el siguiente ejemplo, que crea un ArrayList de números y los imprime en pantalla:

ArrayList <Integer> lista= new ArrayList (                                                                 Arrays.asList ( 1,2,3,4,5,6 ) );
for  ( Integer elemento : lista ) {
   System.out.println( elemento );
}

Este fragmento de código compila y ejecuta como se espera. El problema está en el uso de Integer dentro del ciclo for. En cada iteración del ciclo se crea una instancia de Integer, cuando no  es necesario. El siguiente código compila exactamente igual sin el problema mencionado:

for ( int elemento : lista)  {
   System.out.println( elemento );
}

Aunque este tema parece sencillo es indispensable para el correcto uso y creación de métodos genéricos, del que seguiré escribiendo próximamente.

Referencias:

Java Generics and Collections,

Naftalin & Wadler,

O’Reilly

Advertisements
Boxing, unboxing y autoboxing en Java

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s