Genéricos#

Clases e Iterfaces Genéricas#

Los genéricos permiten detectar errores en tiempo de compilación en lugar de en tiempo de ejecución.

Los genéricos permiten definir clases, interfaces y métodos que funcionen con cualquier tipo de dato (primitivo o complejo) sin perder la seguridad de tipos en tiempo de compilación. Esto mejora la reutilización de código y evita el uso excesivo de conversiones de tipo (casting).

../images/Figure19.3.png

Figura 5 Here is my figure caption!#

Ejemplo de genérico en una clase:

public class Caja<T> {
  private T objeto;

  public void setObjeto(T objeto) {
      this.objeto = objeto;
  }

  public T getObjeto() {
      return objeto;
  }

  public static void main(String[] args) {
      Caja<String> caja1 = new Caja<>();
      caja1.setObjeto("Hola Mundo");
      System.out.println(caja1.getObjeto());

      Caja<Integer> caja2 = new Caja<>();
      caja2.setObjeto(123);
      System.out.println(caja2.getObjeto());
  }
}

En este ejemplo, Caja es una clase genérica que puede almacenar cualquier tipo de objeto, ya sea un String o un Integer.

Aquí, representa un tipo genérico formal, que puede ser sustituido más tarde por un tipo concreto real. La sustitución de un tipo genérico se denomina instanciación genérica. Por convención, una sola mayúscula, como E o T, para denotar un tipo genérico formal.

// Probar los códigos aquí

Ejemplo clase stack genérica:

Un tipo genérico puede definirse para una clase o una interfaz. Se debe especificar un tipo concreto cuando se utiliza la clase para crear un objeto o cuando se utiliza la clase o la interfaz para declarar una variable de referencia.

../images/Figure19.4.png

Figura 6 Here is my figure caption!#

import java.util.ArrayList;
public class GenericStack<E> {
  private ArrayList<E> list = new ArrayList<>();
  
  public int getSize() {
    return list.size();
  } //getSize
  
  public E peek() {
    return list.get(getSize() - 1);
  } //peek
  public void push(E o) {
    list.add(o);
  } //push
  public E pop() {
    E o = list.get(getSize() - 1);
    list.remove(getSize() - 1);
    return o;
  } //pop
  public boolean isEmpty() {
    return list.isEmpty();
  } //isEmpty
  @Override
  public String toString() {
    return "stack: " + list.toString();
  }
}
// Probar código aquí

Los constructores no llevan el tipo de dato, esta mal escribir new Stack<E>(), solamente basta con definirlo new Stack<>().

Métodos Genéricos#

Se puede definir un tipo genérico para un método estático.

public class GenericMethodDemo {
  public static void main(String[] args ) {
    Integer[] integers = {1, 2, 3, 4, 5};
    String[] strings = {"London", "Paris", "New York", "Austin"};
    GenericMethodDemo.<Integer>print(integers);
    GenericMethodDemo.<String>print(strings);
  }
  public static <E> void print(E[] list) {
    for (int i = 0; i < list.length; i++)
    System.out.print(list[i] + " ");
    System.out.println();
  }
}
// Probar código aquí

Conclusión#

El uso de genéricos, interfaces e iteradores es fundamental en la programación orientada a objetos en Java, permitiendo crear soluciones más flexibles, reutilizables y mantenibles. El patrón iterador, en particular, facilita la navegación por colecciones sin exponer su implementación interna. Estas herramientas son esenciales para gestionar la complejidad en programas más grandes y modulares.

Recursos Adicionales#