Guía de uso opcional en Java 8

     

    Introducción

    Al escribir código de cualquier tipo en Java, los desarrolladores tienden a trabajar con objetos con más frecuencia que con valores primitivos (int, boolean, etc.). Esto sucede porque en el corazón de la programación orientada a objetos hay cosas: permiten a un programador escribir código abstracto de una manera limpia y estructurada.

    Además, cada objeto en Java puede tener un valor o no. Si lo hace, su valor se almacena en el montón y la variable que estamos usando tiene una referencia a ese objeto. Si el objeto no tiene valor, este falla null – un ocupante especial que no muestra ningún valor.

    El hecho de que todo puede ser null, combinado con la tendencia natural a usar cosas en lugar de primitivas, significa que puede (ya veces lo hace) conducir a algo inesperado. NullPointerException.

    Antes de Optional introdujo una clase en Java 8, como NullPointerException Los errores eran mucho más comunes en la vida diaria de un programador Java.

    En las siguientes secciones, nos centraremos más profundamente en la explicación. Optional y vea cómo se puede utilizar para superar algunos de los problemas comunes de los valores cero.

    La clase electiva

    Un Opcional es esencialmente un contenedor. Está diseñado para almacenar un valor o estar «vacío» si el valor no existe; reemplace el null valor. Como veremos en algunos ejemplos posteriores, este reemplazo es crítico porque permite una verificación nula implícita para todos los objetos que se muestran como Optional.

    Esto significa que ya no es necesario que el programador compruebe explícitamente el valor nulo, sino que lo implementa el propio lenguaje.

    Creando Opciones

    Veamos lo fácil que es crear escenarios. Optional y envolviendo cosas que ya tenemos en nuestras aplicaciones.

    Usaremos nuestra clase personalizada para esto, el Spaceship rango:

    public class Spaceship {
        private Engine engine;
        private String pilot;
    
        // Constructor, Getters and Setters
    }
    

    Y nuestro Engine parece que:

    public class Engine {
        private VelocityMonitor monitor;
    
        // Constructor, Getters and Setters
    }
    

    Y ademas, el VelocityMonitor rango:

    public class VelocityMonitor {
        private int speed;
    
        // Constructor, Getters and Setters
    }
    

    Estas clases son arbitrarias y solo tienen un punto, no hay una implementación real detrás de ellas.

    de ()

    El primer acercamiento a la creación Optionalutilizando el .of() método, refiriéndose a un objeto no nulo pasado:

    Spaceship falcon = new Spaceship();
    Optional<Spaceship> optionalFalcon = Optional.of(falcon);
    

    Si el falcon fue null, el método .of() tuve que NullPointerException.

    Pecado Optional, intentando acceder a cualquiera de los dominios o métodos falcon (asumiendo que null), si no se realiza la verificación nula, el programa se bloqueará.

    Le Optional, la .of() aviso de método null valorar y lanzar el NullPointerException inmediatamente, tal vez también bloquee el programa.

    Si el programa falla en ambos enfoques, ¿por qué molestarse siquiera Optional?

    El programa no caería en un lugar más profundo del código (ya que viene falcon) pero en el primer uso (inicial) null objeto, minimizando el daño potencial.

    ofNullable ()

    Ambos falcon esta permitido null, en vez de .of() método, queremos el .ofNullable() método. Funcionan igual si el valor no esnull. La diferencia es obvia cuando la referencia se centra en ella. null en ese caso – el .ofNullable() el método del desprecio es perfecto con este código:

    Spaceship falcon = null;
    Optional<Spaceship> optionalFalcon = Optional.ofNullable(falcon);
    

    vacío ()

    Y finalmente, en lugar de plegar una variable de referencia existente (null o nonull), podemos null valor en contexto Optional. Es como un recipiente vacío que devuelve una muestra vacía. Optional:

    Optional<Spaceship> emptyFalcon = Optional.empty();
    

    Verificar valores

    Después de la creación Optionalsy empaquetar información en ellos, es natural que queramos acceder a ellos.

    Sin embargo, antes de acceder a él, debemos comprobar si hay algún valor, o si hay Optionalestá vacío.

    está presente ()

    Dado que la captura de excepciones es una operación exigente, es mejor utilizar uno de los métodos de la API para comprobar si el valor existe antes de intentar acceder a él, y cambiar el flujo si no.

    Si es así, entonces .get() se puede utilizar un método para acceder al valor. Aunque, más sobre ese método en las partes finales.

    Para comprobar si el valor está presente en el interior Optional, usamos el .isPresent() método. Esto es básicamente un reemplazo para el null-comprueba los viejos tiempos:

    // Without Optional
    Spaceship falcon = hangar.getFalcon();
    if (falcon != null) {
        System.out.println(falcon.get());
    } else {
        System.out.printn("The Millennium Falcon is out and about!");
    }
    
    // With Optional
    Optional<Spaceship> optionalFalcon = Optional.ofNullable(hangar.getFalcon());
    if (optionalFalcon.isPresent()) {
        System.out.println(falcon.get());
    } else {
        System.out.println("The Millennium Falcon is out and about!");
    }
    

    Desde el falcon No puedo estar en el hangar también, podemos esperar null valor, por lo tanto .ofNullable() se utiliza.

    si está presente ()

    Haciendo las cosas aún más fáciles Optional también hay un método condicional que evita completamente la verificación de presencia:

    Optional<Spaceship> optionalFalcon = Optional.ofNullable(hangar.getFalcon());
    optionalFalcon.ifPresent(System.out::println);
    

    Si hay un valor, el contenido se imprime mediante Referencia de método. Si no hay valor en el contenedor, no pasa nada. Es posible que aún desee utilizar el enfoque anterior si desea una definición else {} declaración, sin embargo.

    Esto ilustra lo que mencionamos anteriormente cuando dijimos que null-comprueba con Optional comprendido y aplicado por el sistema de tipos.

    esta vacio ()

    Otra forma de verificar el valor es usar .isEmpty(). Básicamente, llamando Optional.isEmpty() lo mismo que llamar !Optional.isPresent(). No hay ninguna diferencia particular:

    Optional<Spaceship> optionalFalcon = Optional.ofNullable(hangar.getFalcon());
    if (optionalFalcon.isEmpty()) {
        System.out.println("Please check if the Millennium Falcon has returned in 5 minutes.");
    } else {
        optionalFalcon.doSomething();
    }
    

    Nesting Nesting Checks

    Nuestra Spaceship la clase tiene una característica, como se definió anteriormente Engine, que tiene una característica VelocityMonitor.

    Suponiendo ahora que queremos acceder al objeto del monitor de velocidad y obtener la velocidad actual de la nave espacial, teniendo en cuenta que todos estos valores pueden ser null.

    Encontrar la velocidad puede verse así:

    if (falcon != null) {
        Engine engine = falcon.getEngine();
        if (engine != null) {
            VelocityMonitor monitor = engine.getVelocityMonitor();
            if (monitor != null) {
                Velocity velocity = monitor.getVelocity();
                System.out.println(velocity);
            }
        }
    }
    

    El ejemplo anterior muestra lo ordenado que es realizar tales comprobaciones, sin mencionar la cantidad de código delictivo necesario para que las comprobaciones sean viables en primer lugar.

    Una solución alternativa usando Optional haría:

    Velocity velocity = falcon
        .flatMap(Spaceship::getEngine)
        .flatMap(Engine::getVelocityMonitor)
        .map(VelocityMonitor::getVelocity);
    

    Nota: ¿No estás seguro de lo que está pasando arriba? Consulte la explicación a continuación para obtener más detalles.

    Con este tipo de enfoque, no se requieren verificaciones explícitas. Si está vacío en alguno de los objetos Optional, el resultado final también estará vacío Optional.

    Para que este tipo de cosas funcionen, necesitamos nuestras definiciones de Spaceship y Engine clases:

    public class Spaceship {
        private Optional<Engine> engine;
        private String pilot;
    
        // Constructor, Getters and Setters
    }
    
    public class Engine {
        private Optional<VelocityMonitor> monitor;
    
        // Constructor, Getters and Setters
    }
    

    Las definiciones de atributos que hemos cambiado son: ahora están dobladas Optional cosas para hacer posible una solución alternativa de este tipo.

    Esto puede resultar un poco tedioso al principio, pero si se planifica desde el principio, se necesita casi la misma cantidad de esfuerzo para escribirlo.

    Además, después Optional un atributo que no sea un objeto regular indica que el atributo puede existir o no. Tenga en cuenta que esto es bastante útil, ya que no tenemos esos significados semánticos con definiciones de atributos regulares.

    Explicación de muestra

    En esta sección, nos tomaremos un tiempo para explicar el ejemplo anterior con flatMaps y maps. Si lo entiende sin más explicaciones, no dude en omitir esta sección.

    Se realiza la primera llamada a métodos falcon que es una especie de Optional<Spaceship>. Llamando al getEngine el método devuelve una especie de objeto Optional<Engine>. La combinación de estos dos tipos hace que el tipo de objeto devuelto Optional<Optional<Engine>>.

    Dado que queremos ver este objeto como Engine contenedor y hacer más llamadas, necesitamos algún tipo de mecanismo para «quitar la piel exterior» Optional capa.

    Existe tal mecanismo y se le da flatMap. Este método API combina map y el flat operaciones aplicando primero una función a cada uno de los elementos y luego difundiendo el resultado en una secuencia de un solo nivel.

    El es map por otro lado, una función solo se aplica sin difundir la corriente. En nuestro caso, utilice map y flatMap que nos daría Optional<Optional<Engine>> y Optional<Engine> respectivamente.

    Vocación flatMap en una especie de objeto Optional por lo que llevaría a un nivel Optional, lo que nos permite utilizar varias llamadas de métodos similares en sucesión.

    Que nos deja para el finalOptional<Engine>, que queríamos en primer lugar.

    Resultados alternativos

    .si no ()

    El ejemplo anterior se puede ampliar aún más utilizando el orElse(T other) método. El método devolverá el Optional un objeto conocido sólo si tiene un valor interno.

    Si el Optional vacío, el método devuelve el other valor. Básicamente Optional versión del operador ternario:

    // Ternary Operator
    Spaceship falcon = maybeFalcon != null ? maybeFalcon : new Spaceship("Millennium Falcon");
    
    // Optional and orElse()
    Spaceship falcon = maybeFalcon.orElse(new Spaceship("Millennium Falcon"));
    

    Junto con la ifPresent() Método, este tipo de enfoque utiliza las expresiones lambda para hacer que el código sea más legible y menos propenso a errores.

    .orElseGet ()

    En vez de other valor solo como un argumento, podemos
    Proveedor en lugar. La diferencia entre .orElse() y .orElseGet(), aunque esto puede no ser obvio a primera vista:

    // orElse()
    Spaceship falcon = maybeFalcon.orElse(new Spaceship("Millennium Falcon"));
    
    // orElseGet()
    Spaceship falcon = maybeFalcon.orElseGet(() -> new Spaceship("Millennium Falcon"));
    

    Ambos maybeFalcon no hay valor, ambos métodos devolverán un nuevo método Spaceship. En este caso, su comportamiento es el mismo. La diferencia es obvia si maybeFalcon hay valor.

    En el primer caso, el new Spaceship un objeto no se devolverá, pero se creará. Esto sucederá independientemente de si el valor existe o no. En el segundo caso, el new Spaceship creado solo si maybeFalcon no hay ningún valor.

    Es como do-while realiza la tarea independientemente de la while bucle, al menos una vez.

    Esto puede parecer una diferencia insignificante, pero es muy importante si la creación de una nave espacial es una operación exigente. En primer lugar, siempre estamos creando un nuevo objeto, incluso si nunca se utilizará.

    .orElseGet() debería ser elegido en su lugar .orElse() en esos casos.

    .oElseThrow ()

    En lugar de devolver un valor alternativo (como hemos visto en las dos secciones anteriores), podemos lanzar una excepción. Esto se logra con el .orElseThrow() un método que acepta un proveedor en lugar de un valor alternativo que devuelve la excepción por temor a tener que gastarlo.

    Esto puede resultar útil en situaciones en las que el resultado final es muy importante y no debe estar vacío. Quizás la opción más segura es usar una excepción en este caso:

    // Throwing an exception
    Spaceship falcon = maybeFalcon.orElseThrow(NoFuelException::new);
    

    Obtener valores de opcional

    .obtener ()

    Después de ver muchas formas diferentes de verificar y acceder al valor interno Optional, veamos ahora una forma final de encontrar el valor utilizado por algunos de los métodos mostrados anteriormente.

    La forma más sencilla de acceder al valor interior Optional si .get(). Este método devuelve el valor actual o arroja un NoSuchElementException si el valor está ausente:

    Optional<Spaceship> optionalFalcon = Optional.ofNullable(hangar.getFalcon());
    if (falcon.isPresent()) {
        Spaceship falcon = optionalFalcon.get()
    
        // Fly the falcon
    }
    

    Como era de esperar, el .get() modo de no retornonull por ejemplo Spaceship clase y asigna a la falcon objeto.

    Conclusión

    Optional Java se introdujo como una forma de resolver los problemas con null referencias. antes de Optional, todos los objetos podían tener o no tener un valor (es decir, tener null).

    Introducción Optional básicamente hace cumplir null– Verificar por el sistema de tipos de modo que tales verificaciones no tengan que realizarse manualmente.

    Este fue un paso importante para mejorar el lenguaje y su usabilidad al agregar un conjunto adicional de verificaciones de tipo. Usando este sistema en lugar del antiguo nullLa verificación permite escribir un código claro y conciso sin la necesidad de agregar una placa de caldera y realizar comprobaciones agotadoras manualmente.

    .

    5/5 - (1 voto)
    Etiquetas:

    Deja una respuesta

    Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *