JavaRush /Blog Java /Random-ES /Autoboxing y unboxing en Java
Viacheslav
Nivel 3

Autoboxing y unboxing en Java

Publicado en el grupo Random-ES
<h2>Introducción</h2>Un lenguaje de programación, como el lenguaje que habla la gente, vive y cambia, aparecen nuevos fenómenos en él para hacer que el lenguaje sea más cómodo de usar. Y como sabemos, el lenguaje debe expresar convenientemente nuestros pensamientos.
Autoboxing y unboxing en Java - 1
Entonces, en Java SE 5, se introdujo el mecanismo de boxeo/unboxing. Y un tutorial separado de Oracle está dedicado a las características de este medio para expresar pensamientos: Autoboxing y Unboxing . <h2>Boxeo de empaque automático</h2>Veamos un ejemplo de Boxeo de empaque automático. Primero, veamos cómo funciona. Usemos el sitio compilejava.net y creemos una clase:
public class App {
    public static void main(String[] args) {
        Integer portNumber = 8080;
        if (args.length != 0) {
            portNumber = Integer.valueOf(args[0]);
        }
        System.out.println("Port number is: " + portNumber);
    }
}
Código sencillo. Podemos especificar el parámetro de entrada y cambiar el valor del puerto. Como vemos, porque leemos el valor del puerto de Stringlos parámetros, Integerlo obtenemos al pasarlo Integer.valueOf. Por lo tanto, nos vemos obligados a especificarlo no como un tipo primitivo, sino como un tipo de objeto Integer. Y aquí tenemos, por un lado, que tenemos una variable de objeto y el valor predeterminado es una primitiva. Y funciona. Pero no creemos en la magia, ¿verdad? Echemos un vistazo "debajo del capó", como dicen. Descargue el código fuente de compilejava.net haciendo clic en "Descargar ZIP". Después de eso, extraiga el archivo descargado en un directorio y acceda a él. Ahora hagamos: javap -c -p App.classdonde App.class es el archivo de clase compilado para su clase. Veremos contenidos como este:
Autoboxing y unboxing en Java - 2
Este es el mismo notorio "código de bytes". Pero lo que es importante para nosotros ahora es lo que vemos. Primero, la primitiva 8080 se coloca en la pila de ejecución del método y luego se ejecuta Integer.valueOf . Ésta es la “magia” del boxeo. Y por dentro la magia se ve así:
Autoboxing y unboxing en Java - 3
Es decir, en esencia, se tomará Integero se obtendrá uno nuevo Integerdel caché (el caché no es más que una simple matriz de números enteros) dependiendo del valor del número. Naturalmente, Integerno sólo uno tuvo tanta suerte. Hay una lista completa de tipos primitivos relacionados y sus envoltorios (clases que representan primitivos en el mundo de la programación orientada a objetos). Esta lista se encuentra al final del Tutorial de Oracle: " Autoboxing y Unboxing ". Vale la pena señalar de inmediato que las matrices creadas a partir de primitivos no tienen un "envoltorio" sin conectar bibliotecas de terceros. Aquellos. Arrays.asListno hará de int[]para nosotros Listde Integer's. <h2>Unboxing</h2>El proceso inverso al boxeo se llama unboxing unboxing. Veamos un ejemplo de desembalaje:
public class App {

    public static void main(String[] args) {
        if (args.length == 0) {
            System.out.println("Please, enter params");
            return;
        }
      	int value = Math.abs(Integer.valueOf(args[0]));
        System.out.println("Absolute value is: " + value);
    }

}
Math.abssólo acepta primitivas. ¿Qué hacer? La clase contenedora tiene un método especial para este caso que devuelve una primitiva. Por ejemplo, este es el Integermétodo intValue . Si miramos el código de bytes, es así:
Autoboxing y unboxing en Java - 4
Como puedes ver, no hay magia. Todo está dentro de Java. Simplemente funciona "por sí solo". Para nuestra comodidad. <h2>Rastrillo</h2>
Autoboxing y unboxing en Java - 5
Cualquier herramienta, si se utiliza incorrectamente, se convierte en un arma formidable contra sí misma. Y el mecanismo automático de boxeo/unboxing en Java no es una excepción. La primera comparación obvia es a través de ==. Creo que esto está claro, pero veámoslo de nuevo:
public static void main(String[] args) {
    Integer inCacheValue = 127;
    Integer inCacheValue2 = 127;
    Integer notInCache = 128; // new Integer(129)
    Integer notInCache2 = 128; // new Integer(129)
    System.out.println(inCacheValue == inCacheValue2); //true
    System.out.println(notInCache == notInCache2); //false
}
En el primer caso, el valor se toma del Integercaché de valores (ver explicación de Boxing arriba), y en el segundo caso se creará un nuevo objeto cada vez. Pero aquí vale la pena hacer una reserva. Este comportamiento depende del límite superior de la caché ( java.lang.Integer.IntegerCache.high ). Además, este límite puede cambiar debido a otras configuraciones. Puede leer la discusión sobre este tema en stackoverflow: ¿ Qué tamaño tiene la caché de enteros? Naturalmente, los objetos deben compararse utilizando iguales: System.out.println(notInCache.equals(notInCache2)); el segundo problema asociado con el mismo mecanismo es el rendimiento. Cualquier boxeo en Java equivale a crear un nuevo objeto. Si el número no está incluido en los valores de la caché (es decir, de -128 a 127), se creará un nuevo objeto cada vez. Si de repente el empaquetado (es decir, el empaquetado) se realiza en un bucle, esto provocará un enorme aumento de objetos innecesarios y el consumo de recursos para el trabajo del recolector de basura. Por lo tanto, no seas demasiado imprudente al respecto. Un tercer rastrillo, no menos doloroso, surge del mismo mecanismo:
public static void check(Integer value) {
    if (value <= 0) {
        throw new IllegalStateException("Value is too small");
    }
}
En este código, la persona claramente intentaba no superar el error. Pero no hay ningún control para null. Si se trata de la entrada null, en lugar de un error comprensible obtendremos uno incomprensible NullPointerException. Porque a modo de comparación, Java intentará ejecutarse value.intValuey fallar, porque... valuevoluntad null. <h2>Conclusión</h2>El mecanismo de boxeo/unboxing permite al programador escribir menos código y, a veces, ni siquiera pensar en convertir de primitivos a objetos y viceversa. Pero eso no significa que debas olvidar cómo funciona. De lo contrario, puedes cometer un error que quizás no aparezca de inmediato. No debemos confiar en partes del sistema que no están completamente bajo nuestro control (como el límite de los enteros). Pero no te olvides de todas las ventajas de las clases contenedoras (como Integer). A menudo, estas clases contenedoras tienen un conjunto de métodos estáticos adicionales que mejorarán su vida y su código será más expresivo. He aquí un ejemplo de puesta al día:
public static void main(String[] args) {
    int first = 1;
    int second = 5;
    System.out.println(Integer.max(first, second));
    System.out.println(Character.toLowerCase('S'));
}
La conclusión correcta de todo es que no hay magia, hay algún tipo de realización. Y no todo será siempre lo que esperamos. Por ejemplo, no hay empaquetado: System.out.println("The number is " + 8); el compilador optimizará el ejemplo anterior en una línea. Es decir, es como si escribieras “El número es 8”. Y en el siguiente ejemplo tampoco habrá embalaje:
public static void main(String[] args) {
    System.out.println("The number is " + Math.abs(-2));
}
¿Cómo puede ser cuando printlntomamos un objeto como entrada y necesitamos conectar las líneas de alguna manera? Líneas... sí, por eso no existe un packaging como tal. Existen Integermétodos estáticos, pero algunos de ellos lo son package. Es decir, no podemos usarlos, pero en el propio Java se pueden usar activamente. Este es exactamente el caso aquí. Se llamará al método getChars, que crea una matriz de caracteres a partir del número. Nuevamente, no hay magia, solo Java). Entonces, en cualquier situación poco clara, solo hay que observar la implementación y al menos algo encajará. #viacheslav
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION