Comprender los genéricos – Blog de Whizlabs

En el artículo anterior, aprendimos sobre los conceptos básicos de los genéricos y su uso. En este artículo, bajaremos un nivel y comprenderemos la semántica de los genéricos y cómo el compilador maneja el código genérico.

En resumen, los genéricos proporcionan seguridad en tiempo de compilación. Cuando proporcionamos la información de tipo entre corchetes angulares (<… >), el compilador utilizará este tipo de información para asegurarse de que no coloquemos objetos con tipos diferentes en la colección. Esta verificación de seguridad ocurre únicamente en tiempo de compilación.

Los genéricos también se pueden usar para parámetros de métodos para la seguridad de tipos de argumentos, y también se pueden usar genéricos para tipos de retorno. El siguiente ejemplo rápido resume el uso de genéricos en parámetros de método y tipos de retorno.

void procesoMisCadenas(Lista listaDeCadenas) {
listOfStrings.add(“otraCadena”); }

Los ejemplos anteriores muestran la seguridad de tipos para los argumentos del método. El compilador no aceptará ningún valor que no sea String para insertarse en listOfStrings.

Lista pública getMyStrings() {
Lista miLista = nueva ArrayList();
//…
devolver miLista; }

El ejemplo anterior muestra la seguridad de tipos para el tipo de retorno del método. Nuevamente, el compilador no aceptará ninguna lista como tipo de retorno que no contenga objetos String.

Hasta ahora, sólo hemos utilizado tipos Java predefinidos (por ejemplo, String) con genéricos. También podemos usar cualquier tipo de clase personalizada en los genéricos. Por ejemplo, el siguiente fragmento de código crea una ArrayList de empleados. Esta lista acepta cualquier objeto que sea de tipo Empleado y ningún otro tipo. Si intentamos insertar cualquier otro tipo en esta lista, se producirá un error en tiempo de compilación.

Lista empleados = new ArrayList();

Debido a que hemos definido nuestra lista de empleados con genéricos de tipo Empleado, el compilador solo aceptará que se inserten objetos Empleado en la lista.

Empleado e1 = nuevo Empleado('John', 'Doe');
Empleado e2 = nuevo Empleado('John', 'Smith');
empleados.add(e1);
empleados.add(e2);

Supongamos que queremos recuperar el primer objeto empleado de la lista de empleados, el siguiente código funcionará perfectamente sin conversión.

Empleado primer empleado = empleados.get(0); // Esto seguramente nos dará una instancia de Empleado.

La instancia firstEmployee es de tipo Empleado, no se requiere una conversión explícita.

Genéricos y compatibilidad con versiones anteriores

Dado que no se requiere una conversión explícita cuando usamos genéricos para almacenar objetos de cualquier tipo en Colecciones, en algunos casos, es posible que tengamos un código no genérico que realice la conversión de tipos. Uno de los aspectos de diseño de los genéricos es admitir código heredado (que es código no genérico). En este tipo de situaciones, el elenco explícito no hará ningún daño. El siguiente ejemplo realizará una conversión de tipo colección segura.

Lista misNúmeros = new ArrayList();
misNúmeros.add(1);
misNúmeros.add(2);
Entero miNúmero = (Entero)misNúmeros.get(1); // Esto está bien, la conversión explícita no hará daño aunque no es necesaria.

Un punto importante a recordar sobre los genéricos es que JVM (Java Virtual Machines) no tiene idea sobre la seguridad de tipos de nuestras colecciones. Este tipo de información de seguridad no está disponible en tiempo de ejecución. Proporcionamos genéricos junto con información de tipo solo al compilador, para que pueda aplicar el tipo apropiado en el momento de la compilación.

El compilador eliminará toda la información de tipo justo antes de convertir el código fuente a código de bytes (y puede insertar conversiones explícitas si es necesario). Con el tiempo, el código de bytes compilado se verá exactamente igual que el código no genérico, aunque hemos proporcionado información de seguridad de tipos al compilador. Este proceso se llama “borrado de tipo”. Eso significa que la información de seguridad de tipos genéricos no está disponible en el código de bytes ya compilado, por lo tanto, no está disponible en tiempo de ejecución, solo está destinada a la seguridad en tiempo de compilación.

La razón detrás de no tener información de seguridad de tipos en tiempo de ejecución es simple: brindar soporte para código heredado. Dado que es prácticamente imposible modificar el código existente para convertirlo a genérico, la verificación de seguridad de tipos ocurre solo en el momento de la compilación del código recién desarrollado. El código heredado (o no genérico) se ejecutará porque en tiempo de ejecución todo el código es, en última instancia, el mismo.

De hecho, no necesitamos seguridad en tiempo de ejecución hasta que mezclemos código genérico y no genérico. En tales escenarios, el compilador emitirá una advertencia como se muestra a continuación.

Javac MiProcesadorEmpleado.java

Nota: MyEmployeeProcessor.java utiliza operaciones no marcadas o inseguras. Nota: Vuelva a compilar con –Xlint: sin marcar para obtener más detalles.

Si volvemos a compilar con –Xlint:unchecked, veremos el error exacto que genera una operación potencialmente insegura.

Como mencionamos anteriormente, cuando mezclamos código genérico y no genérico, el compilador nos dará la advertencia y las advertencias NO son fallas del compilador. Es responsabilidad del desarrollador tratar las advertencias de manera adecuada y cambiar el código si es necesario. El motivo de esta advertencia es que el compilador sospecha que existe la posibilidad de que se inserte un objeto de tipo incorrecto en la colección.

Comprenda más sobre genéricos en el curso de capacitación Whizlabs OCPJP 6.

Publicaciones Similares

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *