Está en la página 1de 10

Clases y métodos genéricos

Introducción

Los métodos genéricos permiten especificar, con una simple


declaración, un conjunto de métodos relacionados.

Las clases genéricas (e interfaces) permiten especificar, con una


declaración única, un conjunto de tipos relacionados .

Los métodos genéricos usan los conceptos de herencia y


sobrecarga.

Los genéricos tambien proporcionan seguridad de tipos en tiempo


de compilación y permiten capturar errores de tipo en esa
instancia.

Por ejemplo, se podría escribir un método genérico para ordenar


un arreglo de objetos, luego invocar al método genérico con
arreglos en integer, double, String, etc. a fin de ordenar el arreglo
de elementos. El compilador realizará el chequeo de tipos para
asegurar que el arreglo pasado al método de ordenamiento,
contiene el mismo tipo de elementos.

Motivación para métodos genéricos

La sobrecarga de métodos a menudo se usa para ejecutar


operaciones similares con diferentes tipos de datos.

Cuando el compilador encuentra una llamada a método, intenta


localizar una declaración de método que tenga un nombre y
parámetros que coincidan con los tipos en la llamada.

printArray( integerArray ); // pass an Integer array


printArray( doubleArray ); // pass a Double array
printArray( characterArray ); // pass a Character array

public static void printArray( Integer[] inputArray )


{
// display array elements
for ( Integer element : inputArray )
System.out.printf( "%s ", element );

System.out.println();
} // end method printArray

Se pueden reemplazar los tipos de elementos en cada método


con un nombre genérico (T por convención). Se puede
reemplazar el tipo de elementos del arreglo de los métodos
(integer, double, caracter) con un tipo genérico único.

Ver el archivo:
MetodosSobrecargados.java

Métodos genéricos: implementación y traducción en


tiempo de compilación

Si las operaciones realizadas por varios métodos sobrecargados


son idénticas para cada tipo de argumento, ellas pueden ser
definidos de forma más compacta y conveniente usando un
método genérico.

Una declaración única de método genérico, puede ser llamada


con argumentos de tipos de datos diferentes. Basado en el tipo de
argumento pasado al método genérico, el compilador maneja
cada llamada al método adecuadamente.

Todas las declaraciones de métodos genéricos tienen (por


convención) una secuencia de tipo (<T>) antes del tipo de
retorno del método. Esta sección, de parámetros de tipo,
contiene uno o más parámetros de tipo separados por comas. Un
parámetro de tipo, también conocido como variable de tipo, es un
identificador que especifica un nombre de tipo genérico.

public static < T > void printTwoArrays( T[] array1, T[] array2 )
Los parámetros de tipo pueden ser usados como tipos de retorno,
variables locales, etc. Actúan como referencias para los tipos de
argumentos pasados al método genérico, que se conocen como
argumentos de tipo actuales.

Los parámetros de tipo pueden representar sólo tipos de


referencia (no tipos primitivos como int, double o char). El
nombre de los parámetros de tipo a través de la declaración del
método debe corresponder a aquellos declarados en la sección de
parámetros de tipo.

El nombre de parámetros de tipo puede ser declarado sólo una


vez en la sección de parámetros de tipo, pero puede aparecer
más de una vez en la lista de parámetros del método.

public static <T> void imprimirArreglo(T[] arregloEntrada)


{
// Muestra los elementos del arreglo
for (T elemento : arregloEntrada)

Si el compilador encuentra una llamada a un método, determina


el tipo de argumento e intenta localizar un método con el mismo
nombre y con un parámetro del mismo tipo. Si no se encuentra,
el compilador busca si existe un método genérico con el mismo
nombre y usa el parámetro de tipo para representar el arreglo de
elementos.

Cuando el compilador traduce el método genérico a bytecode, se


remueve la sección de parámetros de tipo y se reemplazan los
parámetros de tipo con los parámetros actuales. Este proceso se
conoce como erasure. Por defecto todos los tipos genéricos son
reemplazados con el tipo Object (a menos que se especifique lo
contrario).
Asuntos adicionales de traducción en tiempo de
compilación: métodos que usan un parámetro de tipo
como retorno

Si los elementos de una clase genérica se quieren comparar, no


se pueden usar operadores directos (por ejemplo >) ya que estos
no trabajan en tipos de referencias. Es posible comparar dos
objetos de la misma clase si esa clase implementa la interface
genérica Comparable<T>. Todos las clases del tipo envoltorio
(wrapper) para datos primitivos implementan a Comparable.

public static <T extends Comparable<T>> T maximo(T x, T y, T


z)

El método compareTo, que devuelve 0 si los objetos son iguales,


un entero negativo si el primer objeto es menor que el segundo, o
un entero positivo si el primer objeto es mayor que el segundo.

Los argumentos pasados a un método genérico deben ser de


referencia. Si se pasa un tipo primitivo, el sistema hará un
autoboxing.

Los objetos Comparable pueden ser usados con los métodos de


búsqueda y clasificación de la clase Collections.

El erasure realizado por el compilador en la interfaz comparable,


reemplazaría los parámetros quedando de la siguiente forma:
Adicionalmente se introducen los Cast necesarios para cada tipo
de dato antes de la llamada para asegurar que el valor de retorno
sea el esperado

(Integer) maximum( 3, 4, 5 )

Ver el archivo:

PruebaMetodoGenerico.java
PruebaMaximo.java

Sobrecargar métodos genéricos

Un método genérico puede ser sobrecargado con otros métodos


genéricos con diferentes parámetros (por ejemplo en cantidad).
También puede sobrecargarse por métodos no genéricos si existe
una declaración de método más precisa que la genérica.

Clases genéricas

Las clases genéricas proporcionan un medio para describir una


clase en una forma independiente de tipos que maneja. Por
ejemplo, el concepto de una estructura de datos como una pila,
se puede tratar de forma independiente de si se trata de una pila
de int, double, etc.

Una declaración de clase genérica se ve como una declaración de


clase no genérica, excepto que el nombre de la clase es seguido
por una sección de parámetros de tipo. La sección de parámetro
de tipo de una clase genérica puede tener uno o más parámetros
de tipo separados por comas.
// Create a Stack< Double > and a Stack< Integer >
Stack< Double > doubleStack = new Stack< Double >( 5 );
Stack< Integer > integerStack = new Stack< Integer >();

Cuando una clase genérica es compilada, el compilador realiza el


erasure en los parámetros de tipo de la clase y los reemplaza con
su límite superior.

Los parámetros de tipo no pueden ser usados en una declaración


de clase estática.

Cuando instanciamos un objeto de una clase genérica, los tipos


especificados entre menor y mayor después del nombre de la
clase son conocidos como argumentos de tipo. El compilador los
usa para reemplazar los parámetros de tipo tal que él pueda
realizar chequeo de tipos e insertar operaciones de moldeo
cuando sea necesario.

Ver los archivos:


Pila.java
ExcepcionPilaVacia.java
PruebaPila1.java
PruebaPila2.java

Tipos crudos

Es posible instanciar una clase genérica sin especificar un


argumento de tipo. En este caso, los nuevos objetos de la clase se
dice que tienen un tipo crudo, el compilador implícitamente usa el
tipo Object (o el límite superior del parámetro de tipo).

Stack objectStack = new Stack( 5 ); // no type-argument specified

También se puede asignar la clase pasando argumentos de tipo,


por ejemplo, Double.

Stack rawTypeStack2 = new Stack< Double >( 5 );

En este caso la asignación es permitida porque los elementos en


Stack<Double> son objetos (la clase Double es una subclase
indirecta de objetos).

Stack< Integer > integerStack = new Stack( 10 );

De forma similar, la variable Stack especifica un tipo de


argumento en su declaración. Aunque la asignación es permitida
no es segura porque un Stack de tipos crudos (que se asignará),
puede contener otros valores distintos a Integer. El compilador
dará warning.

Ver el archivo:
PruebaTipoCrudo.java

Comodines en métodos que aceptan parámetros de tipo

Suponemos que queremos implementar el método genérico sum


que totaliza los números de una colección (ArrayList). Ingresamos
los números en la colección, aunque los números deben ser
autoboxed en clases de envoltura porque las clases genéricas no
usan elementos primitivos (por ejemplo, un valor int es colocado
en un objeto Integer). Como los números de la colección pueden
ser int o double declaramos la ArrayList con el parámetro
<Number> que corresponde a una superclase tanto de Integer
como de Double.
El método doubleValue de Number obtiene el valor primitivo
subyacente de Number como un valor double.

El ejemplo funciona porque se envía como argumento un


ArrayList<Number> y el parámetro en el método sum es un
ArrayList<Number>. Pero si enviamos un argumento con
ArrayList<Integer>, no dará error.

List<Number> miLista = new List<Integer>( );

En List<Number> sólo pueden entrar objetos que pertenezcan a


la clase Number y no sus subclases (Integer, Double, etc.).
Argumentos de tipo comodines permiten especificar parámetros
que actúan como pertenecientes a subclases o superclases de los
tipos originales.

Un argumento tipo comodín es denotado por la marca de


interrogación (?), que representa un “tipo desconocido”.

ArrayList< ? extends Number >

En el ejemplo, el comodín extiende la clase Number, lo que


significa que el comodín tiene como límite superior a Number.
Así, el argumento desconocido puede ser Number o una de sus
subclases.

ArrayList< ? super Integer >

En este caso se pueden usar las clases Integer o superiores (como


Number).

A causa de que un comodin (?) no es un nombre de parámetro de


tipo, usted no puede usarlo como un nombre de tipo a través del
cuerpo de un método.

Si un comodin es especificado sin un límite superior, entonces


sólo los métodos de tipo Object pueden ser invocados sobre
valores del tipo comodin.

Los métodos que usan comodines como argumentos de tipo no


pueden ser usados para agregar elementos a una colección
referenciada por el parámetro.

Ver los archivos:


TotalNumeros.java
PruebaComodin.java

Genéricos y herencia: notas

Una clase genérica puede ser derivada desde otra clase genérica.
Una clase genérica puede ser derivada desde una clase no
genérica. Por ejemplo, Object es una superclase directa o
indirecta de cada clase genérica.

Un método genérico en una subclase puede sobreescribir un


método genérico en una superclase si ambos métodos tienen la
misma firma.

También podría gustarte