Java reflection y los tipos genéricos (y II)

Después de mucho pegarme con esto conseguí que me funcionase. Debo decir que no me gusta nada el hecho de no poder saber el tipo con el que se ha creado un genérico.

Para que se comprenda qué es lo que he hecho y para qué lo necesitba tengo que decir que en la beca tengo una aplicación realizada en C++ que tiene impletentado su propio tipo de seriado (La palabra en español para serializar es seriado. Como en C++ no hay mecanismos de introspección (como java reflection, system.reflection, etc.), el modo de hacerlo es un poco más feo.

Seriado en C++

El mecanismo de seriado que he utilizado en C++ ha consistido en crear una clase abstracta con dos métodos abstractos, uno se seria los datos a un canal (puede ser un fichero, la red, MPI, etc.) y otro que realiza la operación inversa:

/**
* Clase abstracta que define los dos métodos necesarios para poder
* seriar cualquier clase por un canal de almacenamiento o de
* comunicaciones.
*/
class Serializable {
   /**
   * Seria el objeto a través de flujo os.
   */
   void writeObject(OutputStream &os) = 0;
   /**
   * Lee los datos del objeto desde el flujo is.
   */
   void readObject(InputStream &is) = 0;
};

Es importante hacer notar que cualquier mecanismo de seriado exige que la clase seriable tenga un constructor vacío, para permitir regenerar todos los datos a partir del mecanismo de seriado.

El problema del mecanismo anterior es que el programador debe saber qué es lo que se va a transmitir y, por tanto, sabe qué tipo de dato le va a llegar. Esto no sucede con el sistema de seriado de java, que transmite el tipo de dato a través del canal.

Cuando quise ponerme a realizar esto mismo en java pensé que si ya tenía un mecanismo que me decía qué era lo que tenía que leer, no sería necesario implementar readObject y writeObject en cada clase, ya que puedo consultar cuales son los atributos de la clase. Pero el problema sobrevino cuando llegaron los genéricos.

Los genéricos en Java

Los genéricos en Java funcionan igual que las plantillas de C++, pero con una salvedad. En C++ las plantillas se transforman en una implementación distinta para cada tipo con el que se generase la plantilla, en Java sólo existe una implementación con el tipo más simple de la plantilla (si no se especifica nada, es Object).

Lo anterior significa que si yo tengo un vector de simulaciones, en C++ se creará una implemtanción específica de vector que sólo trabaja con simulaciones (y ningún otro tipo). En Java no sucede lo mismo. En Java sólo hay una implementación que será de tipo Object (todos heredan de Object) pero el control de que sea una simulación se realiza en tiempo de compilación. Esto supone que cuando se ejecute la aplicación ya no conoce si es correcto el dato que se le está pasando, ha sido el compilador el que hico la comprobación. Esto supone que no puedo regenerar de la forma deseada el vector.

Solución actual

En estos momentos he conseguido que funcione. Lo primero que hice fue ponerlo todo igual que lo tenía en C++, pero en Java. De este modo, todas las clases seriables deberán tener un readObject y un writeObject. Y para solucionar el tema de los genéricos hice lo siguiente:

Cree el tipo vector genérico que es seriable por este mecanismo. Para garantizar que funcionará he formazado a la clase a ser abstracta. Con lo que comenté en la anterior entrada sobre cómo obtener el tipo genérico, que dije que no funcionaba, pues sí funciona, pero no como yo esperaba. Para que funcione hay que heredar de la clase genérica de este modo:

abstract class VectorSeriable {
   void readObject(InputStream is) {
   /* Leo utilizando el tipo del generíco como se dijo en la anterior entrada */
   }
   void writeObject(OutputStream os) {
   /* Grabo los objetos */
   }
}
 
/**
* Al heredar de este modo, estamos creando una clase VectorSimulaciones
* que es idéntica a VectorSerializable, pero que ya conoce cual es el tipo
* de su genérico.
*/
class VectorSimulaciones extends VectorSeriable {}

Como se puede apreciar, esto es igual que utilizat typedef en C++. De este modo he conseguido que funcione. Ahora debería ser posible eliminar los metodos readObject y writeObject para automatizar todo ya que es bastante feo tenerlos en cada clase.

Deja un comentario

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