Prefacio
Los punteros nulos son las excepciones más comunes y molestas que odiamos. Para evitar que los punteros nulos sean excepciones, no debe escribir muchos juicios no nulos en su código.
Java 8 presenta una nueva clase opcional. Para evitar la ocurrencia de punteros nulos, no es necesario escribir una gran cantidad de juicios if(obj!=null) , siempre que tenga que cargar los datos en opcional, es un contenedor que envuelve el objeto.
Se dice que ningún programador que haya encontrado una excepción de puntero nulo no es un programador de Java, y NULL ha causado muchos problemas. Java 8 presenta una nueva clase llamada java.util.Optional para evitar muchos problemas causados por NULL.
Veamos qué daño puede causar una referencia nula. Primero cree una computadora de clase, la estructura se muestra en la figura a continuación:
¿Qué sucede cuando llamamos al siguiente código?
Versión de cadena = Computer.getSoundCard (). GetUSB (). GetVersion ();
El código anterior parece estar bien, pero muchas computadoras (como Raspberry Pi) en realidad no tienen tarjetas de sonido, por lo que llamar getSoundcard() definitivamente lanzará una excepción de puntero nulo.
Un método regular pero malo es devolver una referencia nula para indicar que la computadora no tiene una tarjeta de sonido, pero esto significa que el método getUSB () se llamará en una referencia vacía, que obviamente lanzará una excepción de control durante el programa en ejecución, lo que hace que el programa deje de funcionar. Piénselo, ¿qué tan vergonzoso es aparecer de repente cuando su programa se ejecuta en una computadora con cliente?
La gran informática Tony Hoare escribió una vez: "Creo que las citas nulas se crearon en 1965, lo que resultó en miles de millones de dólares en pérdidas. La mayor tentación para mí al usar citas nulas fue que era fácil de implementar".
Entonces, ¿cómo podemos evitar las excepciones de puntero nulo cuando el programa se está ejecutando? Debe estar alerta y verificar constantemente los posibles punteros nulos, como este:
Versión de cadena = "Desconocido"; if (Computer! = Null) {Soundcard Soundfard = Computer.getSoundCard (); if (SoundCard! = NULL) {USB USB = SOUNDCARD.GETUSB (); if (usb! = null) {versión = usb.getVersion (); }}} Sin embargo, puede ver que el código anterior tiene demasiados cheques nulos y que toda la estructura del código se vuelve muy fea. Pero tenemos que usar este juicio para asegurarnos de que no habrá punteros nulos cuando el sistema se esté ejecutando. Es simplemente molesto juzgar si hay muchas referencias vacías en nuestro código de negocios, y también conduce a una mala legibilidad de nuestro código.
Si olvida verificar si el valor está vacío, las referencias nulas también tienen grandes problemas potenciales. En este artículo, demostraré que usar referencias nulas como una representación donde los valores no existen es una mala manera. Necesitamos un mejor modelo que indique que el valor no existe, en lugar de usar referencias nulas nuevamente.
Java 8 introdujo una nueva clase llamada java.util.Optional<T> , que se inspiró en el lenguaje Haskell y el idioma Scala. Esta clase puede contener un valor arbitrario, como se muestra en la figura y el código a continuación. Puede pensar en opcional como un valor que puede contener un valor. Si Opcional no contiene un valor, entonces está vacío, como se muestra en la figura a continuación.
Computadora de clase pública {TARDICIÓN DE SOUNDILLA PRIVADA <CARD> SOUNDIENT; Public Opcional <SoundCard> getSoundCard () {...} ...} public class Soundfard {private opcional <Sb> usb; Public Opcional <SB> getUSB () {...}} public class USB {public String getVersion () {...}} El código anterior muestra que una computadora puede reemplazar una tarjeta de sonido (la tarjeta de sonido puede o no existir). La tarjeta de sonido también puede incluir un puerto USB. Este es un método de mejora, y el modelo puede reflejar más claramente que un valor dado puede no existir.
Pero, ¿cómo lidiar con el objeto Optional<Soundcard> ? Después de todo, lo que desea obtener es el número de puerto USB. Es muy simple. La clase opcional contiene algunos métodos para tratar si el valor existe. En comparación con las referencias nulas, la clase opcional lo obliga a manejar si el valor está relacionado, evitando así las excepciones de puntero nulo.
Cabe señalar que la clase opcional no reemplaza las referencias nulas. Por el contrario, para que la API diseñada sea más fácil de entender, cuando ve la firma de una función, puede determinar si el valor que se transmite a la función puede no existir. Esto le solicita que abra la clase opcional para manejar el valor real.
Adoptar el modo opcional
Después de decir mucho, ¡echemos un vistazo a algún código! Primero veamos cómo usar opcional para reescribir la detección de referencias nulas tradicionales. Al final de este artículo, comprenderá cómo usar opcional.
Nombre de cadena = Computer.FlatMap (Computer :: GetSoundCard) .flatmap (Tarjeta de sonido :: getUSb) .map (USB :: getVersion) .orelse ("desconocido"); Crear objetos opcionales
Se puede crear un objeto opcional vacío:
Opcional <SoundCard> SC = Opcional.Empty ();
Lo siguiente es crear un valores opcionales que contengan no nulos:
Talla de sonido Soundcard = new Soundcard (); Opcional <SoundCard> Sc = Opcional.of (Soundfard);
Si la tarjeta de sonido es nula, la excepción del puntero nulo se lanzará inmediatamente (esto es mejor que simplemente lanzarla cuando obtenga el atributo de la tarjeta de sonido).
Al usar OfNullable, puede crear un objeto opcional que pueda contener referencias nulas:
Opcional <SoundCard> sc = opcional.ofnullable (tarjeta de sonido);
Si la tarjeta de sonido es una referencia nula, el objeto opcional está vacío.
Procesamiento de valores en opcional
Ahora que hay un objeto opcional, puede llamar al método correspondiente para manejar si existe el valor en el objeto opcional. En comparación con la detección nula, podemos usar el método ifpresent (), como este:
Opcional <Card -Soundcard> Soundcard = ...; Soundcard.ifpresent (System.out :: println);
De esta manera, no hay necesidad de hacer una detección nula. Si el objeto opcional está vacío, entonces no se imprimirá ninguna información.
También puede usar el método isPresent() para ver si el objeto opcional realmente existe. Además, también hay un método get () que devuelve los valores incluidos en el objeto opcional, si está presente. De lo contrario, se lanzará una Ejecución Nosuchelement. Estos dos métodos se pueden usar juntos como los siguientes para evitar excepciones:
if (SoundCard.IsPresent ()) {System.out.println (SoundCard.get ());} Sin embargo, este método no se recomienda (no tiene mejoría en comparación con la detección nula). A continuación discutiremos las formas habituales de trabajo.
Devuelve valores predeterminados y operaciones relacionadas
Al encontrarse con NULL, una operación regular es devolver un valor predeterminado, que puede usar expresiones ternarias para implementar:
Tapa de sonido Tapa de sonido = Maybesoundcard! = NULL? Maybesoundcard: nueva tarjeta de sonido ("Basic_Sound_Card"); Si usa el objeto opcional, puede usar orElse() para anular. Cuando opcional está vacío orElse() puede devolver un valor predeterminado:
Tapa de sonido de sonido = maybesoundcard.orelse (nueva tarjeta de sonido ("defaut")); Del mismo modo, cuando la opcional está vacía, Orelsethrow () se puede usar para arrojar excepciones:
Talla de sonido Soundfard = maybesoundcard.orelsethrow (ilegalstateException :: new);
Use el filtro para filtrar valores específicos
A menudo llamamos un método de objeto para juzgar sus propiedades. Por ejemplo, es posible que deba verificar si el número de puerto USB es un valor específico. Por razones de seguridad, debe verificar si el uso médico que señala a USB es nulo y luego llame getVersion() , como este:
USB usb = ...; if (usb! = Null && "3.0" .equals (usb.getVersion ())) {System.out.println ("OK");} Si usa Opcional, puede usar la función Filtro para reescribir:
Opcional <Sb> quizásusb = ...; quizásusb.filter (USB -> "3.0" .equals (usb.getVersion ()) .IfPresent (() -> system.out.println ("ok")); El método de filtro requiere un predicado opuesto como parámetro. Si el valor en opcional existe y satisface la predicción, la función de filtro devolverá un valor que satisface la condición; De lo contrario, se devolverá un objeto opcional vacío.
Use el método de mapa para extraer y convertir datos
Un patrón común es extraer algunas propiedades de un objeto. Por ejemplo, para un objeto de tarjeta de sonido, es posible que deba obtener su objeto USB y luego determinar su número de versión. Por lo general, nuestra implementación es así:
if (SoundCard! = NULL) {USB USB = SOUNDCARD.GETUSB (); if (usb! = null && "3.0" .equals (usb.getversion ()) {system.out.println ("ok");}} Podemos usar el método de mapa para anular esta detección nula y luego extraer el objeto del tipo de objeto.
Opcional <SB> USB = MayBesoundCard.Map (TARFARD :: GETUSB);
Esto es lo mismo que usar la función MAP usando Stream. El uso de la transmisión requiere pasar una función como parámetro a la función de mapa, y la función pasada se aplicará a cada elemento en la secuencia. Cuando transmite espacio y tiempo, no pasa nada.
El valor contenido en opcional será convertido por la función pasada (aquí hay una función que obtiene USB de la tarjeta de sonido). Si el objeto opcional es espacio-tiempo, no pasará nada.
Luego, combinamos el método de mapa y el método de filtro para filtrar tarjetas de sonido con el número de versión USB, no 3.0.
Maybesoundcard.map (Tarjeta de sonido :: getUSB) .filter (USB -> "3.0" .Equals (USB.GetVersion ()) .IfPresent (() -> System.out.println ("OK")); De esta manera, nuestro código comienza a parecerse un poco a lo que dimos al principio, sin detección nula.
Pasar objeto opcional usando la función platera
Ahora, se ha introducido un ejemplo de cómo refactorizar el código que usa opcional. Entonces, ¿cómo debemos implementar el siguiente código de manera segura?
Versión de cadena = Computer.getSoundCard (). GetUSB (). GetVersion ();
Tenga en cuenta que el código anterior está extrayendo otro objeto de un objeto, que se puede implementar utilizando la función MAP. En el artículo anterior, configuramos un objeto Optional<Soundcard> en la computadora, y la tarjeta de sonido contiene un objeto Optional<USB> , para que podamos refactorizar el código de esta manera
Versión de cadena = Computer.map (Computer :: GetSoundCard) .map (TARFARD SOUND :: GetUSB) .map (USB :: GetVersion) .Orelse ("Desconocido"); Desafortunadamente, el código anterior compila errores, entonces, ¿por qué? La variable de la computadora es de tipo Optional<Computer> , por lo que no tiene problemas para llamar a la función del mapa. Sin embargo, getSoundcard() devuelve un objeto Optional<Soundcard> , que devuelve un objeto de tipo Optional<Optional<Soundcard>> . Después de que se llama a la función del segundo mapa, la llamada a getUSB() se vuelve ilegal.
La siguiente figura describe este escenario:
La implementación del código fuente de la función MAP es la siguiente:
public <u> opcional <u> map (function <? Super t, "extiende u> mapper) {objects.requirenonnull (mapper); if (! isPresent ()) return vacía (); else {return Oppectional.ofNullable (mApper.apply (valor)); }} Se puede ver que la función del mapa hará que Cam Optional<Optional<Soundcard>> Optional.ofNullable()
Opcional proporciona la función FlatMap, que está diseñada para convertir el valor del objeto opcional (como una operación de mapa) y luego comprimir un nivel opcional de dos niveles en uno. La siguiente figura muestra la diferencia entre los objetos opcionales en la conversión de tipos llamando al mapa y al mapas:
Entonces podemos escribir esto:
Versión de cadena = Computer.FlatMap (Computer :: GetSoundCard) .flatmap (Tarjeta de sonido :: getUSB) .map (USB :: getVersion) .orelse ("desconocido"); El primer plano de plano asegura que el retorno sea Optional<Soundcard> en lugar de Optional<Optional<Soundcard>> , y el segundo platero implementa la misma función para que la devolución sea Optional<USB> . Tenga en cuenta que map() se llama la tercera vez, porque getVersion() devuelve un objeto de cadena en lugar de un objeto opcional.
Finalmente reescribimos el código feo de cheques nulos anidados que acabamos de comenzar, lo cual es altamente legible, y también evita la aparición de excepciones de puntero nulo.
Resumir
En este artículo, adoptamos la nueva clase java.util.Optional<T> proporcionada por Java 8. La intención original de esta clase no es reemplazar las referencias nulas, sino ayudar a los diseñadores a diseñar mejores API. Simplemente lea la firma de la función y sepa si la función acepta un valor que puede o no existir. Además, opcional lo obliga a encender opcional y luego manejar si el valor existe, lo que hace que su código evite las posibles excepciones de puntero nulo.
De acuerdo, lo anterior es todo el contenido de este artículo. Espero que el contenido de este artículo tenga cierto valor de referencia para el estudio o el trabajo de todos. Si tiene alguna pregunta, puede dejar un mensaje para comunicarse. Gracias por su apoyo a Wulin.com.