Métodos genéricos
Parte de la sección Programación Orientada a Objetos del Journey de Java de Coddy — lección 55 de 87.
Mientras que las clases genéricas permiten que clases enteras funcionen con diferentes tipos, a veces solo necesitas que un único método sea genérico. Un método genérico declara su propio parámetro de tipo, independientemente de cualquier genérico a nivel de clase.
El parámetro de tipo aparece antes del tipo de retorno en la firma del método:
public class Utility {
public static <T> void printArray(T[] array) {
for (T element : array) {
System.out.println(element);
}
}
}
String[] names = {"Alice", "Bob"};
Integer[] numbers = {1, 2, 3};
Utility.printArray(names); // Funciona con String[]
Utility.printArray(numbers); // Funciona con Integer[]Observa el <T> colocado entre los modificadores y el tipo de retorno. Esto declara T como un parámetro de tipo para este método específico. El compilador infiere el tipo real a partir de los argumentos que pasas.
Los métodos genéricos también pueden devolver el tipo genérico:
public static <T> T getFirst(T[] array) {
if (array.length > 0) {
return array[0];
}
return null;
}
String first = getFirst(new String[]{"a", "b"}); // Devuelve "a"Puedes usar múltiples parámetros de tipo cuando sea necesario:
public static <K, V> void printPair(K key, V value) {
System.out.println(key + ": " + value);
}
printPair("Age", 25); // String e Integer
printPair(1, "First"); // Integer y StringLos métodos genéricos son particularmente útiles en las clases de utilidad donde se necesitan operaciones flexibles y reutilizables sin necesidad de crear una clase genérica.
Desafío
FácilConstruyamos un kit de herramientas de utilidad que muestre la flexibilidad de los métodos genéricos. Crearás una clase con métodos reutilizables que funcionen con cualquier tipo, demostrando cómo los métodos genéricos pueden existir independientemente de las clases genéricas.
Organizarás tu código en dos archivos:
ArrayUtils.java: Crea una clase de utilidad que contenga métodos genéricos para trabajar con arreglos. Tu clase debe tener tres métodos genéricos estáticos:getLast- Un método genérico que toma un arreglo de tipoTy devuelve el último elemento. Si el arreglo está vacío, devuelvenull.swap- Un método genérico que toma un arreglo de tipoTy dos índices enteros, luego intercambia los elementos en esas posiciones. Este método no devuelve nada.printWithLabel- Un método genérico con dos parámetros de tipoKyV. Toma una etiqueta de tipoKy un valor de tipoV, luego los imprime en el formato:[label]: [value]Main.java: ¡Pon a trabajar tus métodos genéricos! Recibirás cuatro entradas: dos palabras (Strings) y dos números (enteros).Primero, crea un arreglo
String[]que contenga ambas palabras. UsagetLastpara obtener el último elemento e imprime:Last word: [result]A continuación, crea un arreglo
Integer[]que contenga ambos números. Llama aswappara intercambiar los elementos en los índices 0 y 1, luego imprime:After swap: [first], [second]Finalmente, demuestra el método genérico de dos parámetros llamando a
printWithLabeldos veces:- Una vez con la primera palabra como etiqueta y el primer número como valor
- Una vez con el primer número como etiqueta y la segunda palabra como valor
Recibirás cuatro entradas en orden: primera palabra, segunda palabra, primer número, segundo número.
Observa cómo cada método declara su(s) propio(s) parámetro(s) de tipo con <T> o <K, V> antes del tipo de retorno. El compilador infiere los tipos reales a partir de los argumentos que pasas; ¡no es necesario especificarlos explícitamente al llamar a los métodos!
Hoja de referencia
Un método genérico declara su propio parámetro de tipo, independientemente de cualquier genérico a nivel de clase. El parámetro de tipo aparece antes del tipo de retorno en la firma del método.
Sintaxis básica de un método genérico:
public static <T> void printArray(T[] array) {
for (T element : array) {
System.out.println(element);
}
}El <T> se coloca entre los modificadores y el tipo de retorno. El compilador infiere el tipo real a partir de los argumentos que pases.
Los métodos genéricos pueden devolver el tipo genérico:
public static <T> T getFirst(T[] array) {
if (array.length > 0) {
return array[0];
}
return null;
}Se pueden utilizar múltiples parámetros de tipo:
public static <K, V> void printPair(K key, V value) {
System.out.println(key + ": " + value);
}
printPair("Age", 25); // String and Integer
printPair(1, "First"); // Integer and StringLos métodos genéricos son útiles en clases de utilidad para operaciones flexibles y reutilizables sin necesidad de crear una clase genérica.
Pruébalo tú mismo
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// Leer entradas
String firstWord = scanner.nextLine();
String secondWord = scanner.nextLine();
int firstNumber = scanner.nextInt();
int secondNumber = scanner.nextInt();
// TODO: Crear un arreglo String[] que contenga ambas palabras
// TODO: Usar ArrayUtils.getLast para obtener el último elemento
// Imprimir: "Last word: [result]"
// TODO: Crear un arreglo Integer[] que contenga ambos números
// TODO: Llamar a ArrayUtils.swap para intercambiar los elementos en los índices 0 y 1
// Imprimir: "After swap: [first], [second]"
// TODO: Llamar a ArrayUtils.printWithLabel con firstWord como etiqueta y firstNumber como valor
// TODO: Llamar a ArrayUtils.printWithLabel con firstNumber como etiqueta y secondWord como valor
}
}
Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Programación Orientada a Objetos
1Fundamentos de OOP
Archivos externosIntroducción a la OOPClases vs. ObjetosLa palabra clave thisMétodosCampos (Atributos)Método constructorSobrecarga de constructoresResumen - Calculadora simple4Herencia
Herencia básica (extends)La palabra clave superSobrescritura de métodos (@Override)Encadenamiento de constructoresLa clase ObjectHerencia simple y multinivelPor qué no hay herencia múltiple de clasesResumen - Jerarquía de empleados7Métodos especiales y la clase Object
Método toString()equals() y hashCode()Método clone()compareTo() y ComparableInterfaz ComparatorResumen - Ordenamiento personalizado2Modificadores de acceso y encapsulamiento
Visión general de los niveles de accesoMétodos Getter y SetterOcultamiento de informaciónLa palabra clave finalResumen - Gestor de cuentas bancarias5Polimorfismo
Fundamentos de sobrecarga de métodosSobrescritura de métodos (tiempo de ejecución)Upcasting y DowncastingEl operador instanceofClases y métodos abstractosResumen - Calculadora de figuras8Conceptos avanzados de POO
Composición vs. HerenciaAgregación vs. ComposiciónClases internas, anidadas y anónimasEnums y métodos de EnumRecords (Java 16+)Clases selladas (Java 17+)11Patrones de diseño - Parte 1
Introducción a los patrones de diseñoPatrón SingletonPatrón FactoryPatrón BuilderPatrón ObserverPatrón Strategy3Propiedades de clase y miembros estáticos
Variables de instancia vs. estáticasMétodos estáticosBloques estáticosConstantes (static final)Repaso - Contador y utilidad6Interfaces y Clases Abstractas
Introducción a las InterfacesImplementación de InterfacesImplementación de Múltiples InterfacesMétodos Default y Static en InterfacesClases Abstractas vs InterfacesInterfaces FuncionalesResumen - Sistema de Pagos9Genéricos
Introducción a los genéricosClases genéricasMétodos genéricosParámetros de tipo delimitadosComodines (?, extends, super)Resumen - Contenedor genérico12Patrones de diseño Parte 2
Patrón CommandPatrón AdapterPatrón DecoratorPatrón Template MethodPatrón StatePatrón CompositePatrón Iterator