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ícatePropó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()
conget()
- Usa alternativas comoorElse()
oifPresent()
- Prefiere
orElseGet()
sobreorElse()
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:
- Busca una orden por su ID
- Verifica que esté pagada
- Obtiene el cliente asociado
- Verifica que tenga dirección
- Extrae la dirección
- Verifica que la dirección sea válida
- 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()
yfilter()
. 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.
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
Gestión de errores y excepciones
CRUD en Java de modelo Customer sobre un ArrayList
Clases abstractas
Listas
Métodos de la clase String
Streams: reduce()
API java.nio 2
Polimorfismo
Pattern Matching
Streams: flatMap()
Llamada y sobrecarga de funciones
Métodos referenciados
Métodos de la clase String
Representación de Fecha
Operadores lógicos
Inferencia de tipos con var
Tipos de datos
Estructuras de iteración
Streams: forEach()
Objetos
Funciones lambda
Uso de Scanner
Tipos de variables
Streams: collect()
Operadores aritméticos
Arrays y matrices
Clases y objetos
Interfaz funcional Consumer
CRUD en Java de modelo Customer sobre un HashMap
Interfaces
Enumeraciones Enums
API Optional
Interfaz funcional Function
Encapsulación
Interfaces
Uso de API Optional
Representación de Hora
Herencia básica
Clases y objetos
Interfaz funcional Supplier
HashMap
Sobrecarga de métodos
Polimorfismo de tiempo de ejecución
OOP en Java
Sobrecarga de métodos
CRUD de productos en Java
Clases sealed
Creación de Streams
Records
Encapsulación
Streams: min max
Herencia
Métodos avanzados de la clase String
Funciones
Polimorfismo de tiempo de compilación
Reto sintaxis Java
Conjuntos
Estructuras de control
Recursión
Excepciones
Herencia avanzada
Estructuras de selección
Uso de interfaces
Operadores
Variables
HashSet
Objeto Scanner
Streams: filter()
Operaciones de Streams
Interfaz funcional Predicate
Streams: sorted()
Configuración de entorno
Uso de variables
Clases
Streams: distinct()
Streams: count()
ArrayList
Mapas
Datos de referencia
Interfaces funcionales
Métodos básicos de la clase String
Tipos de datos
Clases abstractas
Instalación
Funciones
Excepciones
Estructuras de control
Herencia de clases
La clase Scanner
Generics
Streams: map()
Funciones y encapsulamiento
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
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.