viernes, 19 de junio de 2020

Clases genéricas

Java Generics

Java permite que los programadores puedan crear clases, interfaces y métodos en los que el tipo de datos sobre los que operan se especifica como parámetro. Es decir, de tipos parametrizados.

A estos se les denomina genéricos. Por ejemplo, una clase que maneja estos parámetros se denomina clase genérica, igualmente será con un método o interfaz.

Un método genérico debe ser declarado dentro de una clase o interfaz genérica. Para ello, empleamos la siguiente sintaxis:
   // Clase genérica
   public class Generico<T>

   //Interfaz genérica
   public interface Generico<T,E>

La ventaja del código que emplea estos métodos es que funcionará automáticamente con el tipo de datos pasados a su parámetro de tipo.
   // Instanciación de la clase genérica
   Generico<String> gen = new Generico<>();
   // En este caso el tipo de dato es un String


Estos algoritmos son muy útiles para y se emplean muchísimo en las bibliotecas internas de Java como en las Colecciones o métodos de ordenamiento rápido.

Clase genérica simple- Código

Declaramos el tipo genérico T y un atributo de ese tipo llamado "atributo"
package clases;

public class Generico<T> {

    private T atributo;

    public Generico(T atributo) {
        this.atributo = atributo;
    }

    public String getClase() {
        return String.format("Clase: %s", this.atributo.getClass().getName());
    }

    public String getAtributo() {
        return String.format("Contenido: %s", this.atributo.toString());
    }

}

Prueba y resultado

Al crear objetos de estas clases genéricas podemos emplear 3 formas:
  • Indicar dentro de la sintaxis diamante el tipo
  • Usar el tipo desconocido ?, el constructor recibe el tipo de parámetro
  • Obviar el tipo, el constructor recibe el parámetro y usa su tipo.
package test;

import clases.ContenedorGenerico;
import clases.Generico;
import clases.Operador;

public class Test {

    public static void main(String[] args) {
        Generico<String> gen1 = new Generico<>("Hola");
        System.out.println(gen1.getClase());
        System.out.println(gen1.getAtributo());
        
        Generico<?> gen2 = new Generico(45d);
        System.out.println(gen2.getClase());
        System.out.println(gen2.getAtributo());
        
        Generico gen3 = new Generico(true);
        System.out.println(gen3.getClase());
        System.out.println(gen3.getAtributo());        

    }
    
}

Clase: java.lang.String
Contenido: Hola

Clase: java.lang.Double
Contenido: 45.0

Clase: java.lang.Boolean
Contenido: true


Clase genérica con varios parámetros - Código

Podemos indicar más de un tipo genérico, estos van separados por comas.
package clases;

import java.util.HashMap;
import java.util.Map;

public class ContenedorGenerico<E,F> {

    private Map<E, F> contenedor;

    public ContenedorGenerico() {
        this.contenedor = new HashMap<>();
    }

    public void agregar(E clave, F valor) {
        this.contenedor.put(clave, valor);
    }

    public Map<E, F> getContenedor() {
        return this.contenedor;
    }
}

Prueba y resultado

Las colecciones emplean internamente las clases y métodos genéricos.
package test;

import clases.ContenedorGenerico;
import clases.Generico;
import clases.Operador;

public class Test {

    public static void main(String[] args) {
        
        ContenedorGenerico<Integer,String> contenedor = new ContenedorGenerico<>();
        contenedor.agregar(12, "doce");
        contenedor.agregar(11, "once");
        contenedor.agregar(10, "diez");
        
        contenedor.getContenedor().forEach((k,v)->{
            System.out.println("Clave: "+k);
            System.out.println("Valor: "+v);            
        });
    }
    
}

Clave: 10
Valor: diez

Clave: 11
Valor: once

Clave: 12
Valor: doce


Clase genérica con parámetros limitados - Código

Para limitar el tipo de datos que se pueden recibir se utiliza la palabra reservada "extends" y luego la superclase. Por ejemplo, el siguiente código muestra una clase que solo permite tener parámetros de tipo numéricos.
package clases;

public class Operador<A extends Number, B extends Number> {

    A valorA;
    B valorB;

    public Operador(A valorA, B valorB) {
        this.valorA = valorA;
        this.valorB = valorB;
    }

    public Double getSuma() {
        return this.valorA.doubleValue() + this.valorB.doubleValue();
    }

    public Double getProducto() {
        return this.valorA.doubleValue() * this.valorB.doubleValue();
    }
}

Prueba y resultado

package test;

import clases.ContenedorGenerico;
import clases.Generico;
import clases.Operador;

public class Test {

    public static void main(String[] args) {
        
        Operador op1 = new Operador(4, 5);
        System.out.println("Suma: " + op1.getSuma());
        System.out.println("Producto: " + op1.getProducto());
        
        Operador op2 = new Operador(4.6, 5);
        System.out.println("Suma: " + op2.getSuma());
        System.out.println("Producto: " + op2.getProducto());
        
        Operador op3 = new Operador(2.5, 1.65);
        System.out.println("Suma: " + op3.getSuma());
        System.out.println("Producto: " + op3.getProducto());
    }
    
}

Suma: 9.0
Producto: 20.0

Suma: 9.6
Producto: 23.0

Suma: 4.15
Producto: 4.125


No hay comentarios:

Publicar un comentario