Advanced Java: generics, lambdas and streams

Generics, lambda expressions, the Streams API and the Java 8+ date/time API, with interactive examples.

Introduction to Advanced Java

In this module you'll explore advanced Java features that will let you write more efficient, safe, and modern code. These tools are essential for developing professional applications.

Generics

Parameterized types for reusable, type-safe code

Lambda

Anonymous functions for functional programming

Streams API

Declarative processing of collections

Dates/Times

Modern API for handling dates (Java 8+)

Files

Efficient file reading and writing

Generics (Generics)

Parameterized Types: Write Once, Use Any Type


// CLASE GENÉRICA BÁSICApublic class Caja {
    private T contenido;
    
    public void guardar(T contenido) {
        this.contenido = contenido;
    }
    
    public T obtener() {
        return contenido;
    }
    
    public void mostrarTipo() {
        System.out.println("Tipo de T: " + contenido.getClass().getSimpleName());
    }
}

// CLASE GENÉRICA CON MÚLTIPLES PARÁMETROSpublic class Par {
    private K clave;
    private V valor;
    
    public Par(K clave, V valor) {
        this.clave = clave;
        this.valor = valor;
    }
    
    public K getClave() { return clave; }
    public V getValor() { return valor; }
}

// MÉTODO GENÉRICOpublic class Utilidades {
    public static  boolean esIgual(T obj1, T obj2) {
        return obj1.equals(obj2);
    }
    
    public static > T maximo(T a, T b) {
        return a.compareTo(b) > 0 ? a : b;
    }
}
Wildcards
Upper Bounded

Accepts the specified type or its subclasses

List lista;
Lower Bounded

Accepts the specified type or its superclasses

List lista;
Unbounded

Accepts any type (read-only)

List lista;

Lambda Expressions

Functional Programming in Java

Lambda expressions allow implementing functional interfaces concisely:


// INTERFAZ FUNCIONAL (un solo método abstracto)@FunctionalInterface
interface Operacion {
    int ejecutar(int a, int b);
}

// FORMA TRADICIONAL (clase anónima)Operacion suma = new Operacion() {
    @Override
    public int ejecutar(int a, int b) {
        return a + b;
    }
};

// FORMA LAMBDA (equivalente)Operacion suma = (a, b) -> a + b;
Lambda Lab
Common Functional Interfaces
Predicate<T>

Represents a function that takes one argument and returns boolean

Predicate esLargo = s -> s.length() > 10;
Function<T,R>

Takes one argument and produces a result

Function longitud = s -> s.length();
Consumer<T>

Performs an operation with the argument (without returning)

Consumer imprimir = s -> System.out.println(s);
Supplier<T>

Provides/supplies a result (without receiving arguments)

Supplier random = () -> Math.random();

Streams API

Declarative Processing of Collections

1
Source

Create a Stream from a collection

List lista = Arrays.asList("a", "b", "c");
Stream stream = lista.stream();
2
Intermediate Operations

Transform or filter elements

stream.filter(s -> s.startsWith("a"))
      .map(String::toUpperCase);
3
Terminal Operation

Get the final result

List resultado = stream.collect(Collectors.toList());
Streams Simulator
Input Data (comma-separated):
Intermediate Operations:
Terminal Operation:
Original Source
Intermediate Operations
Final Result
Practical Stream Examples
Filter and Transform
List nombres = Arrays.asList("Ana", "Juan", "María");
List resultado = nombres.stream()
    .filter(n -> n.length() > 3)
    .map(String::toUpperCase)
    .collect(Collectors.toList());
// Result: ["MARÍA"]
Numeric Operations
List numeros = Arrays.asList(1, 2, 3, 4, 5);
int suma = numeros.stream()
    .mapToInt(Integer::intValue)
    .sum();
// Result: 15
Grouping
List palabras = Arrays.asList("java", "python", "java", "c++");
Map conteo = palabras.stream()
    .collect(Collectors.groupingBy(
        Function.identity(),
        Collectors.counting()
    ));
// Result: {java=2, python=1, c++=1}

Dates and Times (Java 8+)

Modern Dates API (java.time)

Old (java.util.Date) - ❌ NOT RECOMMENDED

Date fecha = new Date(); // MutabilidadCalendar cal = Calendar.getInstance();
cal.set(2024, 0, 1); // Enero es 0Date añoNuevo = cal.getTime();

Mutability (not thread-safe)

Confusing API (months from 0 to 11)

No default time zone

Modern (java.time) - ✅ RECOMMENDED

LocalDate hoy = LocalDate.now(); // ImmutabilityLocalDate añoNuevo = LocalDate.of(2024, 1, 1);
LocalTime ahora = LocalTime.now();
LocalDateTime fechaHora = LocalDateTime.now();

Immutability (thread-safe)

Clear and consistent API

Built-in time zones

Dates Lab
LocalDate
LocalTime
LocalDateTime
ZonedDateTime
Date Formatting and Parsing
From String to Date

// Parseo de fechaDateTimeFormatter formatter = 
    DateTimeFormatter.ofPattern("dd/MM/yyyy");
LocalDate fecha = LocalDate.parse("15/01/2024", formatter);
From Date to String

// Formateo de fechaLocalDate hoy = LocalDate.now();
String formato = hoy.format(
    DateTimeFormatter.ofPattern("EEEE dd 'de' MMMM 'de' yyyy")
);
// Result: "lunes 15 de enero de 2024"
Duration and Period

// Diferencia entre fechasLocalDate inicio = LocalDate.of(2024, 1, 1);
LocalDate fin = LocalDate.of(2024, 12, 31);
Period periodo = Period.between(inicio, fin);
// Result: P11M30D (11 months, 30 days)

File Handling

Efficient Reading and Writing

Basic Reading (FileReader)

// Lectura básica (poco eficiente)FileReader fr = new FileReader("archivo.txt");
int caracter;
while ((caracter = fr.read()) != -1) {
    System.out.print((char) caracter);
}
fr.close();

Character-by-character reading

Not buffered

Efficient Reading (BufferedReader)

// Lectura eficiente con bufferBufferedReader br = new BufferedReader(
    new FileReader("archivo.txt")
);
String linea;
while ((linea = br.readLine()) != null) {
    System.out.println(linea);
}
br.close();

Buffered (high performance)

Line-by-line reading

Modern Reading (Java 7+)

// try-with-resources (auto-close)
try (BufferedReader br = Files.newBufferedReader(
        Paths.get("archivo.txt")
)) {
    br.lines().forEach(System.out::println);
} // No necesita close() explícito

Auto-closeable

Uses Streams

Virtual File Editor
Virtual File Content
Write New Content
Append to End of File
File Operations:
Size: 0 bytes
Lines: 0
Words: 0
Practical Examples
Read CSV File

// Leer y procesar archivo CSVtry (BufferedReader br = Files.newBufferedReader(
        Paths.get("datos.csv")
)) {
    br.lines()
      .skip(1) // Saltar cabecera      .map(linea -> linea.split(","))
      .filter(campos -> campos.length == 3)
      .forEach(campos -> {
          String nombre = campos[0];
          int edad = Integer.parseInt(campos[1]);
          String ciudad = campos[2];
          // Procesar datos...      });
}
Write Log File

// Escribir archivo de logtry (BufferedWriter bw = Files.newBufferedWriter(
        Paths.get("app.log"),
        StandardOpenOption.CREATE,
        StandardOpenOption.APPEND
)) {
    LocalDateTime ahora = LocalDateTime.now();
    String mensaje = String.format(
        "[%s] %s: %s%n",
        ahora.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME),
        "INFO",
        "Aplicación iniciada correctamente"
    );
    bw.write(mensaje);
}
Copy File

// Copiar archivo eficientementePath origen = Paths.get("original.txt");
Path destino = Paths.get("copia.txt");

try (InputStream in = Files.newInputStream(origen);
     OutputStream out = Files.newOutputStream(destino)) {
    
    byte[] buffer = new byte[1024];
    int bytesLeidos;
    while ((bytesLeidos = in.read(buffer)) != -1) {
        out.write(buffer, 0, bytesLeidos);
    }
}

Integrated Exercises

Inventory System with Generics

Create an inventory system that uses generics to handle different product types:

  1. Generic class Almacen<T> that stores products of type T
  2. Interface Producto with methods getPrecio() and getNombre()
  3. Classes ProductoFisico and ProductoDigital that implement Producto
  4. Generic method to calculate the total inventory value
  5. Use of wildcards for methods that read products

Text Processor with Lambdas and Streams

Implement a text processor that uses lambdas and streams for text analysis:

  1. Read text file using Files.lines()
  2. Use lambdas to filter, transform and reduce
  3. Calculate statistics: most common words, average length
  4. Generate a formatted report using dates
  5. Write results to a file using BufferedWriter

Event Manager with Dates API

Create a system to manage events using the modern dates API:

  1. Class Evento with name, date/time and duration
  2. Use LocalDateTime and Duration
  3. Filter events by date using lambdas
  4. Calculate remaining time for future events
  5. Sort events chronologically using streams
  6. Export schedule to CSV file

Advanced Logging System

Implement a logging system that combines all the advanced features:

  1. Generic class Logger<T> for different message types
  2. Use lambdas for custom message formatting
  3. Streams for log analysis (error frequency, etc.)
  4. Dates API for each entry's timestamp
  5. Log file rotation by date
  6. Efficient log search using parallel streams

Advanced Concepts Quiz

Question 1: What is the main advantage generics offer in Java?

They let you write code faster
They provide type safety and code reuse
They make code more readable for beginners
They eliminate the need to use exceptions
1 expression in 5

Summary and Resources

What You Learned in This Module

Generics
  • Parameterized classes and methods
  • Compile-time type safety
  • Wildcards: extends, super, unbounded
  • Eliminate the need for explicit casts
Lambda Expressions
  • Concise syntax for functional interfaces
  • Functional programming in Java
  • Common interfaces: Predicate, Function, Consumer, Supplier
  • Method references (::)
Streams API
  • Declarative processing of collections
  • Intermediate and terminal operations
  • Processing pipelines
  • Parallel streams for concurrent processing
Dates and Times
  • API java.time (immutable and thread-safe)
  • LocalDate, LocalTime, LocalDateTime
  • ZonedDateTime for time zones
  • Period and Duration for calculations
File Handling
  • Efficient reading/writing with buffers
  • Try-with-resources for auto-close
  • NIO.2 for modern file operations
  • File processing with streams

Download Resources