[XKCD] Exploits of a Mom

Exploit of a Mom

Envidiando a Groovy

Si hay un operador que extraño en Java despues de estar un rato con Groovy es “?:“. El operador “Safe Navigator”. La idea de este operador es muy simple pero poderosa a la vez como se vera a continuación.

Es muy común abusar de la flexibilidad de Java y la magia de la POO para escribir cosas como:

unObjeto().getUnAtributo().getAtributoDelAtributo().llamarMetodo();

No hace falta decir que si alguno de los objetos del medio llega a ser null terminamos con una NullPointerException grande como una casa. Groovy, asi como la mayoria de los lenguajes dinamicos modernosos, cuenta con un operador denominado “Safe Navigator” que tiene como fin checkear que el objeto no sea null antes de enviarle un mensaje. En el caso que se encuentre con algun null toda la linea devuelve null y a otra cosa mariposa. El codigo anterior quedaria algo asi si tuvieramos este operador en Java.

unObjeto()?:getUnAtributo()?:getAtributoDelAtributo()?:llamarMetodo();

Obviamente no se puede aplicar en todos los escenarios, pero sin dudas es algo que extraño de Groovy.

Generics 201

Los Generics son un invento maravilloso hasta que nos encontramos con que no tienen un comportamiento demasiado polimorfico respecto a los objetos que queremos agregar a las colecciones. Suponiendo que partimos de esta jerarquia.

class Animal { }
class Perro extends Animal { }
class Gato extends Animal { }

Las declaraciones

List<Animal> animales1 = new Arraylist<Perro>();
List<Animal> animales2 = new Arraylist<Gato>();

Son horriblemente invalidas.El compilador unicamente va a aceptar que animales1 o animales2 apunten a Lists de Animal! De hecho podemos declarar lo siguiente que seguiria siendo invalido.

public void vacunar(List<Animal>) {

    //some code here...

}
List<Perro> perros = new ArrayList<Perro>();

vacunar(perros);

El compilador unicamente admite que el parametro pasado a vacunar() sea de tipo List<Animal> nada de polimorfismo ni cosas raras. Esto nos hace dudar de las ventajas de los generics cuando lo que queremos hacer es caminar una colección de animales buscando un comportamiento polimórfico. Bueno, el porque de esta restricción es algo que puede no ser muy fácil de ver al principio. Supongamos que en lugar de hacer todo esto usando colecciones con generics usamos los viejos y queridos arrays.

Animal[] animales = new Perro[5]; //perfectamente legal.
vacunar(animales); //mando Animales a vacunar, en realidad le mando Perros pero sigue siendo legal

Ahora suponiendo que declaramos un metodo vacunar:

public void vacunar(Animal[] animales) {
animales[4] = new Gato();
}

Si bien compila perfectamente, no puedo meter un Gato en un array de Perros, en tiempo de ejecución nos dejaria un gran ArrayStoreException en la pantalla. La idea de los Generics es evitar errores en tiempo de ejecución por hacer chanchadas con las colecciones declarando a priori sobre que clase queremos trabajar. Si se permitiera un comportamiento similar al que hicimos recien con los arrays estariamos tirando por la borda el espiritu de los Generics. ¿Qué hacer entonces?

Bueno, el problema evidentemente esta cuando un metodo trata de agregar algo que no corresponde en una coleccion. Para evitar esto y poder seguir usando colecciones con objetos polimorficos java nos provee de un wildcard ?

Vamos a cambiar el metodo vacunar para utilizar Generics de otra forma

public void vacunar(List<? extends Animal>) {
//some code here...
}

Ahora la llamada a vacunar(perros) es perfectamente valida! “? extends Animal” le dice al compilador “Como parametro te voy a pasar un List que puede tener objetos de cualquier clase que extienda Animal (o que implemente una Interface Animal), sin embargo SOY CONCIENTE DE LOS QUILOMBOS QUE SE PUEDEN HACER AGREGANDO OBJETOS DE OTRA RAMA DE LA JERARQUIA A LA COLECCION ASI QUE NO VOY A AGREGAR NADA USANDO ADD”.

De hecho, el compilador nos va a dar un error, bastante extraño por cierto, si intentamos utilizar el metodo add con la coleccion que pasamos como parametro. Amazing verdad? Ahora podriamos caminar tranquilamente la coleccion para vacunar a todos los perros y gatos, recordando siempre que NO PODEMOS MODIFICAR LA COLECCIÓN, solo recorrerla.

Esto se pone cada vez mejor pero ¿Qué pasa aun asi quiero poder agregar algo a la coleccion utilizando add? Evidentemente Generics nos va a impedir hacer chanchadas como las que se vieron mas arriba entre perros y gatos. Contamos con una variante en la declaracion.

public void vacunar(List<? super Perro> animales) {
//some code here....
}

La palabrita “super” le dice al compilador “Ok, soy conciente de los quilombos que puedo hacer al agregar cosas raras a las colecciones, pero dejame pasar una coleccion de objetos CUYA CLASE SEA SUPERCLASE DE PERRO”. Lo loco de todo esto es que ahora las jerarquias las usamos en el orden inverso (partimos de una subclase y vamos hacia arriba) al que estamos acostumbrados (siempre ir especializando en subclases). el “? super Animal” me permite agregar, dentro del metodo vacunar, objetos a la coleccion usando add pero unicamente si esos objetos son de clase Perro o de una superclase. De esta forma si declaramos un metodo que reciba una coleccion de “? super Perro”

 public void vacunarPerritos(List<? super Perro> perritos) {

perritos.add(new Perro());  //legal

perritos.add(new Animal());  //legal!

perritos.add(new Object()); //legal!

perritos.add(new Gato()); //Error de compilacion
}

El chiste es que ahora perritos puede ser un List de Perro, Animal u Object, y aún así agregarle objetos de clase Perro. Siempre que metamos algo que no sea de clase Perro vamos a tener un error de compilacion evitando de esta forma tener que renegar con RuntimeExceptions que aparecen de la nada.

¿Les siguen gustando los generics?

La Batalla de SCJP

The %b (boolean) conversion character returns true for any non-null or non-boolean argument.

Que una conversion de cualquier valor no-booleano y no-null sea true es la cosa más estupida que escuche en mi vida. Period.

Serialización 101

class Animal {

 public String name;

}

class Dog extends Animal implements Serializable {

  // the rest of the Dog code

}

Because Animal is NOT serializable, any state maintained in the Animal class, even though the state variable is inherited by the Dog, isn’t going to be restored with the Dog when it’s deserialized! The reason is, the (unserialized) Animal part of the Dog is going to be reinitialized just as it would be if you were making a new Dog (as opposed to deserializing one). That means all the things that happen to an object during construction, will happen—but only to the Animal parts of a Dog. In other words, the instance variables from the Dog’s class will be serialized and deserialized correctly, but the inherited variables from the non-serializable Animal superclass will come back with their default/initially assigned values rather than the values they had at the time of serialization.

Entradas y comentarios feeds. 13 queries. 0.268 seconds.

60920 pages viewed, 15 today
29269 visits, 10 today
FireStats icon Powered by FireStats