Java

Tutorial Java: API Optional

Aprende a usar la API Optional en Java 8 para evitar NullPointerException con métodos como of(), orElse(), map() y flatMap().

Aprende Java y certifícate

Propósito y creación de Optional con of(), ofNullable()

La clase Optional fue introducida en Java 8 como una solución elegante a uno de los problemas más comunes en la programación Java: el infame NullPointerException. Este error ocurre cuando intentamos realizar operaciones sobre una referencia que apunta a null, y ha sido la causa de innumerables fallos en aplicaciones Java durante décadas.

El propósito principal de Optional es proporcionar un contenedor que puede o no contener un valor no nulo. En lugar de devolver directamente referencias que podrían ser null, los métodos pueden devolver un Optional que encapsula el resultado, forzando al desarrollador a considerar explícitamente el caso en que no exista un valor.

El problema de los valores nulos

Antes de profundizar en Optional, veamos un escenario típico donde los valores nulos causan problemas:

public String getUserEmailById(Long id) {
    User user = userRepository.findById(id);
    // Si el usuario no existe, user será null
    return user.getEmail(); // Potencial NullPointerException
}

Si userRepository.findById(id) devuelve null, la llamada a user.getEmail() provocará un NullPointerException. La forma tradicional de manejar esto sería:

public String getUserEmailById(Long id) {
    User user = userRepository.findById(id);
    if (user != null) {
        return user.getEmail();
    }
    return null; // Seguimos propagando null
}

Aunque evitamos la excepción, seguimos devolviendo null, trasladando el problema al código que llama a este método.

Creando instancias de Optional

La clase Optional proporciona varias formas de crear instancias, cada una con un propósito específico:

1. Optional.empty()

Crea un Optional vacío que no contiene ningún valor:

Optional<String> empty = Optional.empty();
System.out.println(empty.isPresent()); // Imprime: false

Este método es útil cuando necesitamos devolver un Optional que representa la ausencia de un valor.

2. Optional.of(value)

Crea un Optional que contiene un valor no nulo. Si el valor proporcionado es null, lanzará una NullPointerException:

String name = "Java";
Optional<String> optName = Optional.of(name);
System.out.println(optName.isPresent()); // Imprime: true

// Esto lanzará NullPointerException:
// Optional<String> willThrow = Optional.of(null);

El método of() es adecuado cuando estamos absolutamente seguros de que el valor no es null. Es una forma de documentar esta certeza en el código.

3. Optional.ofNullable(value)

Crea un Optional que puede contener un valor o estar vacío si el valor proporcionado es null:

String name = "Java";
Optional<String> optName = Optional.ofNullable(name);
System.out.println(optName.isPresent()); // Imprime: true

String nullName = null;
Optional<String> optNullName = Optional.ofNullable(nullName);
System.out.println(optNullName.isPresent()); // Imprime: false

Este es el método más flexible y comúnmente utilizado para crear instancias de Optional, ya que maneja correctamente tanto valores no nulos como nulos.

Aplicando Optional a nuestro ejemplo inicial

Veamos cómo podríamos reescribir nuestro ejemplo inicial utilizando Optional:

public Optional<String> getUserEmailById(Long id) {
    Optional<User> userOpt = userRepository.findById(id);
    // Transformamos el Optional<User> en Optional<String>
    return userOpt.map(User::getEmail);
}

Ahora, el método devuelve explícitamente un Optional<String>, indicando claramente que el resultado podría no existir. El código que llama a este método debe manejar explícitamente ambos casos:

Optional<String> emailOpt = getUserEmailById(123L);
String email = emailOpt.orElse("correo@desconocido.com");

Cuándo usar of() vs ofNullable()

La elección entre of() y ofNullable() depende del contexto:

  • Usa of() cuando:
  • Tienes la certeza de que el valor no es null
  • Quieres que se lance una excepción si, inesperadamente, el valor es null (falla rápido)
// Ejemplo: Configuración obligatoria que nunca debería ser null
Optional<Configuration> config = Optional.of(loadMandatoryConfig());
  • Usa ofNullable() cuando:
  • El valor podría ser legítimamente null
  • Quieres manejar elegantemente tanto el caso de valor presente como ausente
// Ejemplo: Búsqueda que puede no encontrar resultados
Optional<User> user = Optional.ofNullable(userRepository.findByUsername(username));

Optional en APIs públicas

Optional es especialmente útil en las interfaces públicas de tu código. Al devolver un Optional, estás comunicando claramente a los usuarios de tu API que deben considerar el caso en que no exista un valor:

public interface UserService {
    // Comunica claramente que el usuario podría no existir
    Optional<User> findByUsername(String username);
    
    // Para métodos que siempre devuelven un valor (o lanzan excepción)
    User getActiveUserById(Long id) throws UserNotFoundException;
}

Integración con Stream API

Una de las ventajas de Optional es su integración natural con la API de Stream. Muchas operaciones de Stream como findFirst(), findAny() o reduce() devuelven Optional:

List<User> users = getUserList();
Optional<User> adminUser = users.stream()
    .filter(user -> "ADMIN".equals(user.getRole()))
    .findFirst();

// Procesamos el resultado solo si existe
adminUser.ifPresent(user -> System.out.println("Admin encontrado: " + user.getName()));

Esta integración permite escribir código más fluido y expresivo al trabajar con colecciones y operaciones que podrían no producir resultados.

Consideraciones de rendimiento

La creación de objetos Optional implica una pequeña sobrecarga en comparación con devolver directamente valores o null. Sin embargo, esta sobrecarga es generalmente insignificante en comparación con los beneficios en términos de seguridad y claridad del código.

Para métodos internos que se llaman con frecuencia en bucles críticos para el rendimiento, podría ser preferible utilizar enfoques tradicionales. Sin embargo, para APIs públicas y la mayoría de los casos de uso, Optional ofrece un excelente equilibrio entre seguridad y rendimiento.

Métodos de acceso seguro: get(), orElse(), orElseGet(), ifPresent()

Una vez que tenemos un objeto Optional, necesitamos extraer su valor de forma segura. Java proporciona varios métodos para acceder al valor contenido en un Optional sin caer en los problemas tradicionales de los valores nulos.

Verificación de presencia con isPresent() e isEmpty()

Antes de acceder al valor, podemos verificar si el Optional contiene un valor:

Optional<String> optName = Optional.ofNullable(getName());

if (optName.isPresent()) {
    // Solo se ejecuta si hay un valor
    System.out.println("Nombre: " + optName.get());
}

// En Java 11+ también podemos usar isEmpty()
if (optName.isEmpty()) {
    System.out.println("No hay nombre disponible");
}

El método isPresent() devuelve true si el Optional contiene un valor no nulo, mientras que isEmpty() (disponible desde Java 11) devuelve true si está vacío.

Extracción directa con get()

El método get() extrae el valor contenido en el Optional, pero lanzará una excepción NoSuchElementException si el Optional está vacío:

Optional<String> optName = Optional.of("Java");
String name = optName.get(); // Devuelve "Java"

Optional<String> empty = Optional.empty();
// empty.get(); // ¡Lanza NoSuchElementException!

Debido a este comportamiento, no es recomendable usar get() directamente sin verificar antes la presencia del valor con isPresent(). Sin embargo, existen alternativas más elegantes que veremos a continuación.

Valores por defecto con orElse()

El método orElse() permite extraer el valor si está presente, o devolver un valor alternativo si el Optional está vacío:

Optional<String> optName = Optional.ofNullable(getUserName());
String name = optName.orElse("Invitado");

Este enfoque es muy útil para proporcionar valores predeterminados cuando no existe un valor. El valor alternativo se especifica directamente como argumento.

Es importante entender que el valor alternativo en orElse() siempre se evalúa, incluso si el Optional contiene un valor:

Optional<String> optName = Optional.of("Java");
String name = optName.orElse(getDefaultName()); // getDefaultName() se ejecuta aunque no se use

Evaluación perezosa con orElseGet()

Para evitar la evaluación innecesaria del valor por defecto, podemos usar orElseGet(), que acepta un Supplier (función que no toma argumentos y devuelve un valor):

Optional<String> optName = Optional.ofNullable(getUserName());
String name = optName.orElseGet(() -> getDefaultName());

Con orElseGet(), la función que genera el valor por defecto solo se ejecuta si el Optional está vacío, lo que puede mejorar el rendimiento cuando la generación del valor por defecto es costosa:

Optional<String> optName = Optional.of("Java");
// getDefaultName() NO se ejecuta porque el Optional tiene valor
String name = optName.orElseGet(() -> getDefaultName());

Comparación entre orElse() y orElseGet()

Veamos un ejemplo que ilustra la diferencia clave entre estos métodos:

public String findUserName(Long userId) {
    Optional<String> optName = userRepository.findNameById(userId);
    
    // Enfoque 1: orElse() - generateDefaultName() siempre se ejecuta
    return optName.orElse(generateDefaultName());
    
    // Enfoque 2: orElseGet() - generateDefaultName() solo se ejecuta si es necesario
    // return optName.orElseGet(() -> generateDefaultName());
}

private String generateDefaultName() {
    System.out.println("Generando nombre por defecto..."); // Operación potencialmente costosa
    return "Usuario" + System.currentTimeMillis();
}

La recomendación general es:

  • Usar orElse() con valores constantes o ya calculados
  • Usar orElseGet() cuando el valor por defecto requiere cálculo o recursos

Lanzamiento de excepciones con orElseThrow()

Cuando la ausencia de un valor representa un error, podemos usar orElseThrow() para lanzar una excepción específica:

User user = userRepository.findById(id)
    .orElseThrow(() -> new UserNotFoundException("Usuario no encontrado con ID: " + id));

Este método es especialmente útil cuando trabajamos con recursos obligatorios cuya ausencia indica un error en la lógica de la aplicación.

Desde Java 10, existe una versión simplificada que lanza NoSuchElementException sin mensaje personalizado:

User user = userRepository.findById(id).orElseThrow(); // Java 10+

Ejecución condicional con ifPresent()

El método ifPresent() permite ejecutar una acción solo si el Optional contiene un valor, sin necesidad de extraerlo explícitamente:

Optional<User> optUser = userRepository.findById(id);
optUser.ifPresent(user -> {
    System.out.println("Usuario encontrado: " + user.getName());
    notificationService.sendWelcomeBack(user);
});

Este enfoque es más limpio que la verificación tradicional con if (optUser.isPresent()) y se integra perfectamente con el estilo funcional de Java.

Ejecución bifurcada con ifPresentOrElse()

Desde Java 9, podemos usar ifPresentOrElse() para ejecutar una acción si el valor está presente, o una acción alternativa si está vacío:

Optional<User> optUser = userRepository.findById(id);
optUser.ifPresentOrElse(
    user -> System.out.println("Usuario encontrado: " + user.getName()),
    () -> System.out.println("Usuario no encontrado con ID: " + id)
);

Este método combina la funcionalidad de ifPresent() y una acción alternativa en una sola llamada, lo que hace que el código sea más conciso.

Ejemplo práctico: Procesamiento de datos de usuario

Veamos un ejemplo completo que utiliza varios métodos de acceso seguro:

public class UserProcessor {
    
    public void processUser(Long userId) {
        Optional<User> optUser = userRepository.findById(userId);
        
        // Obtener nombre con valor por defecto
        String userName = optUser.map(User::getName)
                                .orElse("Anónimo");
        
        // Enviar correo solo si el usuario existe y tiene email
        optUser.map(User::getEmail)
               .ifPresent(emailService::sendWelcomeEmail);
        
        // Obtener nivel de acceso o crear uno básico si no existe
        AccessLevel accessLevel = optUser.map(User::getAccessLevel)
                                        .orElseGet(AccessLevel::createBasicLevel);
        
        // Registrar actividad o lanzar excepción si es un ID crítico
        if (isCriticalId(userId)) {
            User user = optUser.orElseThrow(() -> 
                new SecurityException("Usuario crítico no encontrado: " + userId));
            securityService.logAccess(user);
        }
    }
}

Este ejemplo muestra cómo los diferentes métodos de acceso seguro pueden combinarse para crear un flujo de procesamiento robusto que maneja adecuadamente tanto la presencia como la ausencia de valores.

Patrones de uso recomendados

Para aprovechar al máximo los métodos de acceso seguro de Optional, considera estas recomendaciones:

  • Evita encadenar isPresent() con get() - Usa alternativas como orElse() o ifPresent()
  • Prefiere orElseGet() sobre orElse() cuando el valor por defecto requiere cálculo
  • Usa ifPresent() para efectos secundarios en lugar de extraer el valor para usarlo
  • Considera orElseThrow() para valores obligatorios en lugar de verificaciones manuales

Siguiendo estos patrones, tu código será más conciso, expresivo y menos propenso a errores relacionados con valores nulos.

Operaciones funcionales: map(), flatMap(), filter()

La clase Optional no solo nos permite manejar valores potencialmente nulos de forma segura, sino que también incorpora operaciones funcionales que nos permiten transformar y filtrar su contenido sin necesidad de extraerlo. Estas operaciones siguen el mismo patrón que encontramos en la API de Stream, permitiéndonos escribir código más declarativo y conciso.

Transformación de valores con map()

El método map() nos permite transformar el valor contenido en un Optional si este está presente, aplicando una función que recibe el valor actual y devuelve uno nuevo:

Optional<User> userOpt = userRepository.findById(123L);
Optional<String> emailOpt = userOpt.map(user -> user.getEmail());
// Versión con referencia a método
Optional<String> emailOpt2 = userOpt.map(User::getEmail);

La operación map() solo se ejecuta si el Optional contiene un valor. Si el Optional está vacío, simplemente devuelve otro Optional vacío sin invocar la función de transformación.

Este comportamiento nos permite encadenar transformaciones de forma segura:

Optional<String> usernameOpt = userRepository.findById(123L)
    .map(User::getEmail)
    .map(email -> email.split("@")[0]);

En este ejemplo, si no se encuentra el usuario o si el email es null, obtendremos un Optional vacío sin que se produzca ninguna NullPointerException.

Un caso de uso común es extraer propiedades anidadas de forma segura:

// Sin Optional tendríamos que hacer:
String country = null;
if (user != null && user.getAddress() != null) {
    country = user.getAddress().getCountry();
}

// Con Optional:
Optional<String> countryOpt = Optional.ofNullable(user)
    .map(User::getAddress)
    .map(Address::getCountry);

Aplanamiento de Optional anidados con flatMap()

Cuando trabajamos con métodos que ya devuelven Optional, el uso de map() puede llevarnos a tener un Optional dentro de otro Optional (Optional<Optional<T>>). Para evitar este anidamiento, podemos usar flatMap():

// Supongamos que este método ya devuelve un Optional
Optional<Address> findAddressByUser(User user) {
    // Implementación...
}

// Si usáramos map, obtendríamos Optional<Optional<Address>>
Optional<User> userOpt = userRepository.findById(123L);
Optional<Optional<Address>> nestedAddressOpt = userOpt.map(this::findAddressByUser);

// Con flatMap obtenemos directamente Optional<Address>
Optional<Address> addressOpt = userOpt.flatMap(this::findAddressByUser);

El método flatMap() espera una función que devuelva un Optional y "aplana" el resultado, evitando el anidamiento. Esto es especialmente útil cuando trabajamos con cadenas de operaciones que pueden devolver valores opcionales:

Optional<String> zipCodeOpt = userRepository.findById(123L)
    .flatMap(user -> Optional.ofNullable(user.getAddress()))
    .flatMap(address -> Optional.ofNullable(address.getZipCode()));

Un ejemplo práctico sería la navegación por relaciones en una base de datos:

public Optional<Order> findLatestOrderForUser(Long userId) {
    return userRepository.findById(userId)
        .flatMap(user -> orderRepository.findLatestByUser(user.getId()));
}

Filtrado de valores con filter()

El método filter() nos permite aplicar una condición (predicado) al valor contenido en un Optional. Si el valor está presente y cumple la condición, se devuelve el Optional original; si no cumple la condición o el Optional está vacío, se devuelve un Optional vacío:

Optional<User> adultUserOpt = userRepository.findById(123L)
    .filter(user -> user.getAge() >= 18);

Este método es especialmente útil para validar condiciones sin necesidad de extraer el valor:

// Sin Optional:
User user = userRepository.findUserById(id);
if (user != null && user.isActive() && user.hasPermission("ADMIN")) {
    // Hacer algo con el usuario administrador activo
}

// Con Optional:
userRepository.findById(id)
    .filter(User::isActive)
    .filter(user -> user.hasPermission("ADMIN"))
    .ifPresent(user -> {
        // Hacer algo con el usuario administrador activo
    });

El filtrado también es útil para implementar validaciones de negocio de forma elegante:

public Optional<Product> findAvailableProduct(Long productId) {
    return productRepository.findById(productId)
        .filter(product -> product.getStock() > 0)
        .filter(product -> !product.isDiscontinued());
}

Combinando operaciones funcionales

El verdadero poder de estas operaciones se manifiesta cuando las combinamos para crear flujos de procesamiento complejos:

public Optional<String> getShippingLabelForOrder(Long orderId) {
    return orderRepository.findById(orderId)
        .filter(Order::isPaid)
        .flatMap(order -> customerRepository.findById(order.getCustomerId()))
        .filter(customer -> customer.getAddress() != null)
        .map(customer -> customer.getAddress())
        .filter(address -> address.isValid())
        .map(address -> formatShippingLabel(address));
}

Este código encadena múltiples operaciones de forma que:

  1. Busca una orden por su ID
  2. Verifica que esté pagada
  3. Obtiene el cliente asociado
  4. Verifica que tenga dirección
  5. Extrae la dirección
  6. Verifica que la dirección sea válida
  7. Formatea la etiqueta de envío

Si cualquiera de estas condiciones falla, simplemente se devuelve un Optional vacío, sin necesidad de múltiples verificaciones anidadas.

Patrones avanzados con operaciones funcionales

Combinación de múltiples Optional

Cuando necesitamos combinar valores de múltiples Optional, podemos usar un enfoque funcional:

Optional<User> userOpt = userRepository.findById(userId);
Optional<Preferences> prefsOpt = preferencesRepository.findByUserId(userId);

// Combinamos ambos Optional solo si ambos tienen valor
Optional<UserSettings> settingsOpt = userOpt.flatMap(user -> 
    prefsOpt.map(prefs -> new UserSettings(user, prefs))
);

Ejecución condicional con operaciones en cadena

Podemos implementar lógica condicional compleja de forma declarativa:

return userRepository.findById(userId)
    .filter(User::isActive)
    .map(user -> {
        if (user.isAdmin()) {
            return generateAdminDashboard(user);
        } else {
            return generateUserDashboard(user);
        }
    })
    .orElseGet(() -> generateGuestDashboard());

Uso con colecciones

Las operaciones funcionales de Optional se integran perfectamente con la API de Stream:

List<User> activeAdmins = users.stream()
    .map(userId -> userRepository.findById(userId)) // Stream<Optional<User>>
    .filter(Optional::isPresent)                    // Filtramos los Optional no vacíos
    .map(Optional::get)                             // Extraemos los valores
    .filter(User::isActive)                         // Filtramos usuarios activos
    .filter(user -> user.hasRole("ADMIN"))          // Filtramos administradores
    .collect(Collectors.toList());                  // Recolectamos en una lista

En Java 9+, podemos simplificar esto con el método stream() de Optional:

List<User> activeAdmins = users.stream()
    .map(userId -> userRepository.findById(userId)) // Stream<Optional<User>>
    .flatMap(Optional::stream)                      // Convierte Optional a Stream (vacío o con 1 elemento)
    .filter(User::isActive)
    .filter(user -> user.hasRole("ADMIN"))
    .collect(Collectors.toList());

Consideraciones de diseño

Al trabajar con las operaciones funcionales de Optional, es importante tener en cuenta algunas consideraciones:

  • Evita efectos secundarios en las funciones pasadas a map(), flatMap() y filter(). Estas operaciones están diseñadas para transformar y filtrar valores, no para realizar acciones.

  • Prefiere encadenar operaciones en lugar de anidar condicionales. El encadenamiento hace que el código sea más legible y mantenible.

  • No abuses de Optional para flujos de control complejos. Si la lógica se vuelve demasiado compleja, considera refactorizar en métodos más pequeños o usar patrones de diseño apropiados.

  • Considera el rendimiento cuando encadenes muchas operaciones. Aunque la sobrecarga es generalmente pequeña, en bucles críticos puede ser significativa.

// Ejemplo de código bien estructurado con operaciones funcionales
public Optional<Receipt> processPayment(Long orderId, PaymentMethod method) {
    return orderRepository.findById(orderId)
        .filter(order -> !order.isPaid())
        .flatMap(order -> paymentService.pay(order, method))
        .map(payment -> {
            notificationService.sendPaymentConfirmation(payment);
            return receiptGenerator.generateReceipt(payment);
        });
}

Las operaciones funcionales de Optional nos permiten escribir código más declarativo, expresivo y seguro, reduciendo significativamente la posibilidad de NullPointerException mientras mantenemos un estilo de programación moderno y funcional.

Aprende Java online

Otros ejercicios de programación de Java

Evalúa tus conocimientos de esta lección API Optional con nuestros retos de programación de tipo Test, Puzzle, Código y Proyecto con VSCode, guiados por IA.

Streams: match

Test

Gestión de errores y excepciones

Código

CRUD en Java de modelo Customer sobre un ArrayList

Proyecto

Clases abstractas

Test

Listas

Código

Métodos de la clase String

Código

Streams: reduce()

Test

API java.nio 2

Puzzle

Polimorfismo

Código

Pattern Matching

Código

Streams: flatMap()

Test

Llamada y sobrecarga de funciones

Puzzle

Métodos referenciados

Test

Métodos de la clase String

Código

Representación de Fecha

Puzzle

Operadores lógicos

Test

Inferencia de tipos con var

Código

Tipos de datos

Código

Estructuras de iteración

Puzzle

Streams: forEach()

Test

Objetos

Puzzle

Funciones lambda

Test

Uso de Scanner

Puzzle

Tipos de variables

Puzzle

Streams: collect()

Puzzle

Operadores aritméticos

Puzzle

Arrays y matrices

Código

Clases y objetos

Código

Interfaz funcional Consumer

Test

CRUD en Java de modelo Customer sobre un HashMap

Proyecto

Interfaces

Código

Enumeraciones Enums

Código

API Optional

Test

Interfaz funcional Function

Test

Encapsulación

Test

Interfaces

Código

Uso de API Optional

Puzzle

Representación de Hora

Test

Herencia básica

Test

Clases y objetos

Código

Interfaz funcional Supplier

Puzzle

HashMap

Puzzle

Sobrecarga de métodos

Test

Polimorfismo de tiempo de ejecución

Puzzle

OOP en Java

Proyecto

Sobrecarga de métodos

Código

CRUD de productos en Java

Proyecto

Clases sealed

Código

Creación de Streams

Test

Records

Código

Encapsulación

Código

Streams: min max

Puzzle

Herencia

Código

Métodos avanzados de la clase String

Puzzle

Funciones

Código

Polimorfismo de tiempo de compilación

Test

Reto sintaxis Java

Proyecto

Conjuntos

Código

Estructuras de control

Código

Recursión

Código

Excepciones

Puzzle

Herencia avanzada

Puzzle

Estructuras de selección

Test

Uso de interfaces

Test

Operadores

Código

Variables

Código

HashSet

Test

Objeto Scanner

Test

Streams: filter()

Puzzle

Operaciones de Streams

Puzzle

Interfaz funcional Predicate

Puzzle

Streams: sorted()

Test

Configuración de entorno

Test

Uso de variables

Test

Clases

Test

Streams: distinct()

Puzzle

Streams: count()

Test

ArrayList

Test

Mapas

Código

Datos de referencia

Test

Interfaces funcionales

Puzzle

Métodos básicos de la clase String

Test

Tipos de datos

Código

Clases abstractas

Código

Instalación

Test

Funciones

Código

Excepciones

Código

Estructuras de control

Código

Herencia de clases

Código

La clase Scanner

Código

Generics

Código

Streams: map()

Puzzle

Funciones y encapsulamiento

Test

Todas las lecciones de Java

Accede a todas las lecciones de Java y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.

Instalación De Java

Introducción Y Entorno

Configuración De Entorno Java

Introducción Y Entorno

Tipos De Datos

Sintaxis

Variables

Sintaxis

Operadores

Sintaxis

Estructuras De Control

Sintaxis

Funciones

Sintaxis

Recursión

Sintaxis

Arrays Y Matrices

Sintaxis

Excepciones

Programación Orientada A Objetos

Clases Y Objetos

Programación Orientada A Objetos

Encapsulación

Programación Orientada A Objetos

Herencia

Programación Orientada A Objetos

Clases Abstractas

Programación Orientada A Objetos

Interfaces

Programación Orientada A Objetos

Sobrecarga De Métodos

Programación Orientada A Objetos

Polimorfismo

Programación Orientada A Objetos

La Clase Scanner

Programación Orientada A Objetos

Métodos De La Clase String

Programación Orientada A Objetos

Excepciones

Programación Orientada A Objetos

Records

Programación Orientada A Objetos

Pattern Matching

Programación Orientada A Objetos

Inferencia De Tipos Con Var

Programación Orientada A Objetos

Enumeraciones Enums

Programación Orientada A Objetos

Generics

Programación Orientada A Objetos

Clases Sealed

Programación Orientada A Objetos

Listas

Framework Collections

Conjuntos

Framework Collections

Mapas

Framework Collections

Funciones Lambda

Programación Funcional

Interfaz Funcional Consumer

Programación Funcional

Interfaz Funcional Predicate

Programación Funcional

Interfaz Funcional Supplier

Programación Funcional

Interfaz Funcional Function

Programación Funcional

Métodos Referenciados

Programación Funcional

Creación De Streams

Programación Funcional

Operaciones Intermedias Con Streams: Map()

Programación Funcional

Operaciones Intermedias Con Streams: Filter()

Programación Funcional

Operaciones Intermedias Con Streams: Distinct()

Programación Funcional

Operaciones Finales Con Streams: Collect()

Programación Funcional

Operaciones Finales Con Streams: Min Max

Programación Funcional

Operaciones Intermedias Con Streams: Flatmap()

Programación Funcional

Operaciones Intermedias Con Streams: Sorted()

Programación Funcional

Operaciones Finales Con Streams: Reduce()

Programación Funcional

Operaciones Finales Con Streams: Foreach()

Programación Funcional

Operaciones Finales Con Streams: Count()

Programación Funcional

Operaciones Finales Con Streams: Match

Programación Funcional

Api Optional

Programación Funcional

Transformación

Programación Funcional

Reducción Y Acumulación

Programación Funcional

Mapeo

Programación Funcional

Streams Paralelos

Programación Funcional

Agrupación Y Partición

Programación Funcional

Filtrado Y Búsqueda

Programación Funcional

Api Java.nio 2

Entrada Y Salida Io

Fundamentos De Io

Entrada Y Salida Io

Leer Y Escribir Archivos

Entrada Y Salida Io

Httpclient Moderno

Entrada Y Salida Io

Clases De Nio2

Entrada Y Salida Io

Api Java.time

Api Java.time

Localtime

Api Java.time

Localdatetime

Api Java.time

Localdate

Api Java.time

Executorservice

Concurrencia

Virtual Threads (Project Loom)

Concurrencia

Future Y Completablefuture

Concurrencia

Spring Framework

Frameworks Para Java

Micronaut

Frameworks Para Java

Maven

Frameworks Para Java

Gradle

Frameworks Para Java

Lombok Para Java

Frameworks Para Java

Quarkus

Frameworks Para Java

Ecosistema Jakarta Ee De Java

Frameworks Para Java

Introducción A Junit 5

Testing

Accede GRATIS a Java y certifícate

Certificados de superación de Java

Supera todos los ejercicios de programación del curso de Java y obtén certificados de superación para mejorar tu currículum y tu empleabilidad.

En esta lección

Objetivos de aprendizaje de esta lección

  • Comprender el propósito y la creación de instancias de Optional con of(), ofNullable() y empty().
  • Aprender a acceder de forma segura a los valores de Optional usando métodos como get(), orElse(), orElseGet(), ifPresent() y orElseThrow().
  • Aplicar operaciones funcionales como map(), flatMap() y filter() para transformar y filtrar valores dentro de Optional.
  • Integrar Optional con la API de Streams y entender patrones de uso recomendados para evitar NullPointerException.
  • Evaluar consideraciones de diseño y rendimiento al utilizar Optional en APIs públicas y código funcional.