Java 8, lanzado en marzo de 2014, introdujo una serie de características nuevas y poderosas que han transformado la forma en que los desarrolladores escriben código en Java. Este módulo cubrirá las características más importantes de Java 8, proporcionando explicaciones detalladas, ejemplos prácticos y ejercicios para reforzar el aprendizaje.

Contenido

Expresiones Lambda

Introducción

Las expresiones lambda son una característica clave de Java 8 que permiten tratar la funcionalidad como un argumento del método o almacenar la funcionalidad en una variable. Las lambdas proporcionan una forma clara y concisa de representar un método anónimo.

Sintaxis

(parameters) -> expression

o

(parameters) -> { statements; }

Ejemplo

// Ejemplo de una expresión lambda simple
Runnable runnable = () -> System.out.println("Hola, Mundo!");
runnable.run();

Explicación

En este ejemplo, () -> System.out.println("Hola, Mundo!") es una expresión lambda que implementa la interfaz Runnable.

Ejercicio

Escribe una expresión lambda que tome dos enteros y devuelva su suma.

@FunctionalInterface
interface Suma {
    int sumar(int a, int b);
}

public class LambdaExample {
    public static void main(String[] args) {
        Suma suma = (a, b) -> a + b;
        System.out.println("Suma: " + suma.sumar(5, 3)); // Salida: Suma: 8
    }
}

Streams

Introducción

Los Streams en Java 8 proporcionan una forma moderna y funcional de procesar colecciones de datos. Permiten realizar operaciones como filtrado, mapeo y reducción de manera declarativa.

Ejemplo

import java.util.Arrays;
import java.util.List;

public class StreamExample {
    public static void main(String[] args) {
        List<String> nombres = Arrays.asList("Ana", "Juan", "Pedro", "Maria");
        nombres.stream()
               .filter(nombre -> nombre.startsWith("P"))
               .forEach(System.out::println); // Salida: Pedro
    }
}

Explicación

En este ejemplo, nombres.stream() crea un stream a partir de la lista nombres. Luego, filter(nombre -> nombre.startsWith("P")) filtra los nombres que comienzan con "P", y forEach(System.out::println) imprime cada nombre filtrado.

Ejercicio

Usa Streams para encontrar el número máximo en una lista de enteros.

import java.util.Arrays;
import java.util.List;

public class MaxExample {
    public static void main(String[] args) {
        List<Integer> numeros = Arrays.asList(3, 5, 7, 2, 8);
        int max = numeros.stream()
                         .max(Integer::compare)
                         .get();
        System.out.println("Máximo: " + max); // Salida: Máximo: 8
    }
}

Interfaz Funcional

Introducción

Una interfaz funcional es una interfaz que contiene exactamente un método abstracto. Estas interfaces pueden ser implementadas por expresiones lambda.

Ejemplo

@FunctionalInterface
interface Operacion {
    int operar(int a, int b);
}

public class FunctionalInterfaceExample {
    public static void main(String[] args) {
        Operacion suma = (a, b) -> a + b;
        System.out.println("Suma: " + suma.operar(10, 5)); // Salida: Suma: 15
    }
}

Explicación

En este ejemplo, Operacion es una interfaz funcional con un método abstracto operar. La expresión lambda (a, b) -> a + b implementa esta interfaz.

Ejercicio

Crea una interfaz funcional que tenga un método que reciba un string y devuelva su longitud.

@FunctionalInterface
interface Longitud {
    int obtenerLongitud(String s);
}

public class LongitudExample {
    public static void main(String[] args) {
        Longitud longitud = s -> s.length();
        System.out.println("Longitud: " + longitud.obtenerLongitud("Hola")); // Salida: Longitud: 4
    }
}

Métodos Predeterminados y Estáticos en Interfaces

Introducción

Java 8 permite definir métodos predeterminados y estáticos en interfaces. Los métodos predeterminados proporcionan una implementación predeterminada que las clases pueden usar o sobrescribir.

Ejemplo

interface Vehiculo {
    default void encender() {
        System.out.println("El vehículo está encendido.");
    }

    static void revisar() {
        System.out.println("Revisando el vehículo.");
    }
}

public class Carro implements Vehiculo {
    public static void main(String[] args) {
        Carro carro = new Carro();
        carro.encender(); // Salida: El vehículo está encendido.
        Vehiculo.revisar(); // Salida: Revisando el vehículo.
    }
}

Explicación

En este ejemplo, Vehiculo tiene un método predeterminado encender y un método estático revisar. La clase Carro puede usar estos métodos directamente.

Ejercicio

Crea una interfaz con un método predeterminado que imprima un mensaje de saludo y un método estático que imprima un mensaje de despedida.

interface Saludo {
    default void saludar() {
        System.out.println("Hola, bienvenido!");
    }

    static void despedir() {
        System.out.println("Adiós, hasta luego!");
    }
}

public class SaludoExample implements Saludo {
    public static void main(String[] args) {
        SaludoExample ejemplo = new SaludoExample();
        ejemplo.saludar(); // Salida: Hola, bienvenido!
        Saludo.despedir(); // Salida: Adiós, hasta luego!
    }
}

API de Fecha y Hora

Introducción

Java 8 introdujo una nueva API de fecha y hora en el paquete java.time, que es más intuitiva y menos propensa a errores que las clases Date y Calendar anteriores.

Ejemplo

import java.time.LocalDate;
import java.time.LocalTime;
import java.time.LocalDateTime;

public class DateTimeExample {
    public static void main(String[] args) {
        LocalDate fecha = LocalDate.now();
        LocalTime hora = LocalTime.now();
        LocalDateTime fechaHora = LocalDateTime.now();

        System.out.println("Fecha: " + fecha); // Salida: Fecha: 2023-10-01 (ejemplo)
        System.out.println("Hora: " + hora); // Salida: Hora: 10:15:30.123 (ejemplo)
        System.out.println("Fecha y Hora: " + fechaHora); // Salida: Fecha y Hora: 2023-10-01T10:15:30.123 (ejemplo)
    }
}

Explicación

En este ejemplo, LocalDate.now(), LocalTime.now(), y LocalDateTime.now() obtienen la fecha, hora y fecha y hora actuales, respectivamente.

Ejercicio

Crea un programa que imprima la fecha y hora actuales en un formato específico.

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

public class DateTimeFormatExample {
    public static void main(String[] args) {
        LocalDateTime fechaHora = LocalDateTime.now();
        DateTimeFormatter formato = DateTimeFormatter.ofPattern("dd-MM-yyyy HH:mm:ss");
        String fechaHoraFormateada = fechaHora.format(formato);

        System.out.println("Fecha y Hora Formateada: " + fechaHoraFormateada); // Salida: Fecha y Hora Formateada: 01-10-2023 10:15:30 (ejemplo)
    }
}

Referencias a Métodos

Introducción

Las referencias a métodos proporcionan una forma de referenciar métodos existentes sin tener que invocarlos explícitamente. Son una forma abreviada de expresiones lambda.

Ejemplo

import java.util.Arrays;
import java.util.List;

public class MethodReferenceExample {
    public static void main(String[] args) {
        List<String> nombres = Arrays.asList("Ana", "Juan", "Pedro", "Maria");
        nombres.forEach(System.out::println); // Salida: Ana, Juan, Pedro, Maria
    }
}

Explicación

En este ejemplo, System.out::println es una referencia a método que reemplaza la expresión lambda nombre -> System.out.println(nombre).

Ejercicio

Usa una referencia a método para ordenar una lista de cadenas en orden alfabético.

import java.util.Arrays;
import java.util.List;

public class MethodReferenceSortExample {
    public static void main(String[] args) {
        List<String> nombres = Arrays.asList("Ana", "Juan", "Pedro", "Maria");
        nombres.sort(String::compareToIgnoreCase);
        nombres.forEach(System.out::println); // Salida: Ana, Juan, Maria, Pedro
    }
}

Opcional

Introducción

La clase Optional se introdujo en Java 8 para manejar valores que pueden estar presentes o no, evitando así el uso excesivo de null y reduciendo la posibilidad de errores NullPointerException.

Ejemplo

import java.util.Optional;

public class OptionalExample {
    public static void main(String[] args) {
        Optional<String> nombre = Optional.of("Juan");
        nombre.ifPresent(System.out::println); // Salida: Juan

        Optional<String> nombreVacio = Optional.empty();
        System.out.println(nombreVacio.orElse("Nombre no disponible")); // Salida: Nombre no disponible
    }
}

Explicación

En este ejemplo, Optional.of("Juan") crea un Optional que contiene el valor "Juan". Optional.empty() crea un Optional vacío. ifPresent y orElse son métodos útiles para manejar valores opcionales.

Ejercicio

Crea un programa que use Optional para manejar un valor que puede estar presente o no, y proporciona un valor predeterminado si no está presente.

import java.util.Optional;

public class OptionalDefaultExample {
    public static void main(String[] args) {
        Optional<String> mensaje = Optional.ofNullable(null);
        String mensajeFinal = mensaje.orElse("Mensaje predeterminado");
        System.out.println(mensajeFinal); // Salida: Mensaje predeterminado
    }
}

Conclusión

En este módulo, hemos explorado las características más importantes de Java 8, incluyendo expresiones lambda, streams, interfaces funcionales, métodos predeterminados y estáticos en interfaces, la nueva API de fecha y hora, referencias a métodos y la clase Optional. Estas características no solo hacen que el código sea más conciso y legible, sino que también introducen un enfoque más funcional en la programación en Java. Asegúrate de practicar con los ejemplos y ejercicios proporcionados para consolidar tu comprensión de estos conceptos. ¡Prepárate para el siguiente módulo donde exploraremos más características avanzadas de Java!

Curso de Programación en Java

Módulo 1: Introducción a Java

Módulo 2: Flujo de Control

Módulo 3: Programación Orientada a Objetos

Módulo 4: Programación Orientada a Objetos Avanzada

Módulo 5: Estructuras de Datos y Colecciones

Módulo 6: Manejo de Excepciones

Módulo 7: Entrada/Salida de Archivos

Módulo 8: Multihilo y Concurrencia

Módulo 9: Redes

Módulo 10: Temas Avanzados

Módulo 11: Frameworks y Librerías de Java

Módulo 12: Construcción de Aplicaciones del Mundo Real

© Copyright 2024. Todos los derechos reservados