Tipos genéricos de datos

Ya hemos visto que podemos construir un mundo ...

Pero ¿hay que programarlo desde cero?

¡¡¡Es como barrer el
desierto!!!

Tranquilos, se puede reciclar software.

La clase Object

Algunos problemas

  • Todos los objetos de java son extensiones de la clase Object.
  • Los tipos que contienen datos, como por ejemplo las listas, no están definidas para operar sobre tipos de datos específicos, sino que operan sobre un conjunto homogéneo donde el tipo del conjunto es definido en la declaración.
  • Para poder hacer esto es necesario declarar una lista que acepte objetos de tipo Object.
  • Luego, al recuperar estos elementos de la lista, es necesario añadir los castings pertinentes dependiendo del tipo de dato que haya sido introducido en ella.

La clase Object

Algunos problemas - Ejemplo

  • Vamos a diseñar una clase Caja que pueda guardar datos de cualquier tipo.
  •                 public class Caja {
                      private Object dato;
                      public Caja() { super(); }
                      public Object dame() {
                        return dato;
                      }
                      public void pon(Object x) {
                        dato = x;
                      }
                    }
            

La clase Object

Algunos problemas - Ejemplo

  • ¿Qué pasaría en este caso?
  •             public static void main(String args[]) {
                  Caja c = new Caja();
                  c.pon(“Hola”);
                  Integer n = (Integer) c.dame();
                }
            
  • ¿Error de compilación?
  • ¿Error de ejecución?

La clase Object

Algunos problemas

  • El programador debe recordar que tipo de elemento hay almacenado en la lista y realizar el casting al tipo apropiado cuando lo extraiga de la lista. Extraer un elemento de una lista requiere por tanto dos castings
  • No hay comprobación de tipos en tiempo de compilación. Para el compilador los elementos de todas las listas son de tipo Object por lo que no se pueden comprobar los tipos en tiempo de compilación.
  • Necesaria comprobación explicita de tipos en tiempo de ejecución. Al no diferenciar los tipos en tiempo de compilación se hace necesaria una comprobación de los mismos en tiempo de ejecución para poder detectar posibles errores de tipos. Esto significa que si el desarrollador confunde las dos listas y hace un casting ilegal en un elemento el error no será detectado hasta el tiempo de ejecución.

La clase Object

Algunos problemas

  • Aparición de excepciones (ClassCastException) en tiempo de ejecución. Al aparecer en tiempo de ejecución se hace mucho más difícil la eliminación de los errores de casting que los provocan que si aparecieran en tiempo de compilación
  • Se permite la existencia de clases contenedoras con objetos heterogéneos lo que dará problemas al intentar recuperar los elementos de las listas.

La solución: Clases Genéricas.

Clases Genéricas

Ventajas

  • Comprobación estricta de tipos manteniendo la misma flexibilidad que el enlazado dinámico. Con tipos genéricos se puede alcanzar un polimorfismo similar al usado en el ejemplo anterior, pero con una comprobación estricta de tipos que permite detectar errores en tiempo de compilación.
  • No es necesaria la comprobación de tipos en tiempo de ejecución, lo que redunda en un código con menos castings y por lo tanto más legible y menos propenso a errores.
  • Los tipos genéricos garantizan que las listas contienen solo un conjunto homogéneo de elementos eliminando los errores derivados de la aparición de listas heterogéneas.
  • Hace que el código sea menos ambiguo y más fácil de mantener.

Clases Genéricas

Definición

  • Volvamos al ejemplo anterior
  •             public class Caja <T> {
                  private T dato;
                  public Caja() { super(); }
                  public T dame() { return dato; }
                  public void pon(T x) { dato = x; }
                }
            
  • El valor de la variable T puede ser cualquier clase o interfaz (nunca un tipo primitivo)
  • Cuando se declara un objeto de tipo Caja es necesario indicar el tipo de la variable T
  •           Caja <Integer> c;
            

Clases Genéricas

Definición

  • Volvamos al ejemplo anterior. Ahora el programa principal
  •             public class TestCaja {
                  public static void main(String args[]) {
                    Caja <Integer> c = new Caja <Integer> ();
                    c.pon(new Integer(46));
                    Integer n = c.dame();
                  }
                }
          
  • No es necesario hacer un casting.
  • Cuando se declara un objeto de tipo Caja es necesario indicar el tipo de la variable T
  •         Caja <Integer> c;
          

Clases Genéricas

Definición

  • Volvamos al ejemplo anterior. Ahora el programa principal
  •             public static void main(String args[]) {
                  Caja <String> c = new Caja <String> ();
                  c.pon(“Hola”);
                  Integer n = c.dame();
                }
            
  • Este main ahora da error de compilación.

Métodos Genéricos

  • De forma análoga, es posible definir variables de tipo para un único método.
  •             public static <T> void mostrarArray(ArrayList<T> a) {
                  for (int i = 0; i < a.size(); i++)
                    System.out.println(a.get(i).toString());
                }
            

Ejemplo: la interfaz comparable

Definición

  • Esta interfaz permite la comparación entre dos objetos.
  •             public interface Comparable<T> {
                  public int compareTo(T x);
                  // Devuelve < 0 , si this < x
                  // Devuelve > 0 , si this > x
                  // Devuelve == 0 , si this == x
                }
            

Ejemplo: la interfaz comparable

Ejemplo de uso

  •             public abstract class Figura implements Comparable<Figura> {
                …
                public int compareTo(Figura f) {
                  if (this.area() < f.area()) return -1;
                  else if (this.area() > f.area()) return 1;
                  else return 0;
                }
              …
            

Ejercicio: Implementación de una pila FIFO

  • Debe ser genérica.
  • Usad ArrayList para implementarla.
  • Debe contener los siguientes métodos
    • Métodos a desarrollar:
      • Crear: se crea la cola vacía.
      • Encolar (añadir, entrar, insertar): se añade un elemento a la cola. Se añade al final de esta.
      • Desencolar (sacar, salir, eliminar): se elimina el elemento frontal de la cola, es decir, el primer elemento que entró.
      • Frente (consultar, front): se devuelve el elemento frontal de la cola, es decir, el primer elemento que entró.
      • Vacía: devuelve cierto si la pila está vacía o falso en caso contrario (empty).

Referencias