With the ArrayList class, we have been able to see how we could make a list of any object, as this class uses this Java paradigm, which is called generic programming.

Generic programming consists of writing code that you can reuse for objects of various types, and that is the key: code reuse. This way, you avoid creating several concrete classes for each of the objects. Thus, with ArrayList we can store any type of object.

In the ArrayList class, we modify the type parameter, for example <String>.

It is called generic because you are writing a class that handles objects in general.

We could do the same through inheritance, with a single class capable of handling objects of different types. We already know that in Java we have the Object class from which all others inherit; using this, we could end up using classes of any type, but it brings several disadvantages:

  • Continuous use of casting.
  • Code complexity.
  • No ability to check runtime errors.

In contrast, generic programming offers:

  • Greater code simplicity.
  • Code reuse in numerous scenarios.
  • Compile-time error checking.

Generic programming did not exist until Java version 5.0. If we had to do this without generic classes, we would have some problems...

We can see an example of what an ArrayList class looked like before version 5.0 and its disadvantages.

package ArrayList;

public class ArrayList {

 private Object[] datosElemento;

 private int i=0;

 public ArrayList(int z){

     datosElemento = new Object[z];

 }

 ArrayList() {

     throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.

 }

 public Object get(int i){

     return datosElemento[i];

 }

 public void add(Object o){

     datosElemento[i]=o;

     i++;

 }

}

An example of its usage would be the following.

package ArrayList;

import java.io.*;

public class ArrayList_Ejemplo {

 public static void main(String[] args){

     ArrayList archivos = new ArrayList(6);

     archivos.add("Pepe");

     archivos.add("Maria");

     archivos.add("Juanito");

     archivos.add("Jesus");

     archivos.add("Fran");

     archivos.add(new File("gestion.ves"));

     String nombre = (String) archivos.get(2); //Inconveniente del casting

     System.out.println(nombre);

     //Las siguientes lineas no marcarán errores pero petará en tiempo de ejecución, esto no pasa con la programación generica.

     //String problema = (String) archivos.get(5); //Inconveniente del casting

     //System.out.println("Problema : " + problema);

     ArrayList files = new ArrayList(5);

     files.add(new File("gestion_Pedidos.bla"));

     File archivo = (File) files.get(0);

     System.out.println(archivo);

 }

}

Creating your own generic class

To see it, it will be better to look at an example.

If we have a generic class created by us, called Pareja, where T is any object that will be defined when instantiating the class, just like with ArrayLists. As we can see, instead of placing String or a specific type, we will always put T; this way, when we put the name of the object type it will store between <>, everything becomes that type, allowing us to adapt it to any situation.

For example, Pareja would look like this.

package ClaseGenerica;

public class Pareja<T> {

 private T primero;

 public Pareja(){

        primero=null;

 }

 public void setPrimero(T nuevoValor){

        primero = nuevoValor;

 }

 public T getPrimero(){

     return primero;

 }

}

And we can see a few examples:

package ClaseGenerica;

public class Pareja_Ejemplo {

 public static void main(String[] args){

     Pareja<String> ejemploString = new Pareja<>();

     ejemploString.setPrimero("prueba");

     System.out.println(ejemploString.getPrimero());

     Pareja<Persona> ejemploPersona = new Pareja<>();

     Persona persEjemplo = new Persona("fmesasc");

     ejemploPersona.setPrimero(persEjemplo);

     System.out.println(ejemploPersona.getPrimero());

                               //La clase generica se adapta al elemento.

 }

}

class Persona{

 public Persona(String nombre){

     this.nombre = nombre;

 }

 @Override

 public String toString(){

     return nombre;

 }

 private String nombre;

}

In this way, we can see—using any IDE like NetBeans—that once Pareja<String> is instantiated, all values that were T will become <String>, even in the suggestions.

Generic methods

A generic method is the same as a generic class, but for a method. They can be inside ordinary classes or generic classes. We can see this with the following example.

public class MetodosGenericos {

 public static void main(String[] args){

     String nombres[]={"Pepe","Mónica","Francisco"};

     System.out.println(MisMatrices.getElementos(nombres));

     //Si queremos el elemento menor:

     System.out.println(MisMatrices.getMenor(nombres));

 }

}

class MisMatrices{

 public static <T> String getElementos(T[] a){

     return "El array tiene: " + a.length + " elementos";

 }

//Todos los objetos tendran que tener implementado Comparable, sino dará error.

 public static <T extends Comparable> T getMenor(T[] aux){

     if(aux==null || aux.length==0){

         return null;

     }

     T elementoMenor = aux[0];

     for(int i=1;i<aux.length;i++){

         if(elementoMenor.compareTo(aux[i])>0){

             elementoMenor=aux[i];

         }

     }

     return elementoMenor;

 }

}

Inheritance and wildcard types

Inheritance does not work the same way in generic classes as it does in ordinary classes. For example, a Manager class can inherit from Employee and use substitution: if we can say that a manager is always an employee, Manager inherits from Employee, but an employee does not have to be a manager. However, this substitution principle does not work with generic classes. If we have generic classes for Employee and Manager, we cannot relate them in generic terms. Therefore, we would need to create a method that receives an argument with a generic of type Employee; inside, we can use it via extends from the parent in the inheritance hierarchy—something like Pareja<? extends Empleado> p as the parameter passed, allowing it to be either an employee or a manager directly.