Mostrando entradas con la etiqueta métodos genéricos. Mostrar todas las entradas
Mostrando entradas con la etiqueta métodos genéricos. Mostrar todas las entradas

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