Lombok, una pequeña maravilla

Hace tiempo descubrí lombok  Empecé a encontrármelo en muchos códigos de ejemplo que iba mirando por ahí. Primero eché pestes ¿Por qué tengo que andar bajándome librerías extrañas para ejecutar pequeños códigos de ejemplo tontos?

Sin embargo, veía que se usaba bastante en muchos sitios y yo no la conocía, así que me puse a mirar qué demonios hacía … Y me encantó, descubrí por qué la gente la usa tanto.

En nuestro código java siempre tenemos que andar poniendo métodos set y métodos get, son las buenas costumbres. También constructores con parámetros para rellenar esos atributos de las clases. El famoso método toString() que saca esos atributos por pantalla y que muchas veces nos ayuda en depuración. Los métodos hashcode y equals que siempre van de la mano. Y un largo etcétera.

Los IDE dan soporte para esto. Siempre tienen opciones para generar todo este código automáticamente. Sin embargo, eso tiene una pega. Aparte de la «molestia» de andar por los menús del IDE para encontrar la opción, el código suele quedar muy extenso. Imagina una simple clase con cuatro o cinco atributos que simplemente es una estructura de datos. Hay que meterle cuatro métodos set, cuatro métodos get, el toString, el hashcode y equals de aquellos atributos que consideremos relevantes para decidir si dos estructuras de datos son iguales, constructores. Lo que era una clase de algo más de cuatro líneas puede llegar a tener .. ¿30 líneas? ¿40 líneas? de código generado por el IDE y cuya lectura no nos aporta nada. Solo «complica» la lectura de la clase.

Y eso es lo que evita lombok. Tiene muchas opciones, pero por ejemplo, si a la clase anterior le ponemos la anotación @Data, esa clase automáticamente al compilar tiene todos los métodos que hemos dicho. El código fuente queda solo con sus algo más de cuatro líneas de código, pero tenemos todos estos métodos disponibles.

El código adicional se mete en el .class al compilar. No estará en el .java. Y de aquí las pequeñas pegas de lombok, pero que, en mi opinión, creo que se pueden llevar bien:

  • Como es un proceso de compilado adicional, se suele hacer con lo que se llaman annotation processor. Son procesos que durante el compilado revisan las anotaciones de las clases para hacer «cosas» adicionales. Así que requiere que habilitemos esto en maven/gradle o nuestro IDE favorito.
  • Al no estar el código en los fuentes, el IDE no lo encuentra, por lo que nos dará error si intentamos usar por ejemplo un método set generado por lombok. Afortunadamente los IDE contemplan a lombok. No he mirado el caso de eclipse, pero en idea existe un plugin de lombok y tiene las opciones de habilitar el annotation processor.

Si quieres, aquí tienes más detalles y ejemplos de lombok.

 

 

Seguir las reglas a ciegas

Veo esta duda en StackOverflow en la que preguntan si hay alguna forma de que los getter y setter en java se puedan generar automáticamente desde código, igual que muestra cómo se hace en Javascript. Mirando las respuestas, se llega por ejemplo a proyectos como Project Lombok, en el que poniendo anotaciones @Getter y @Setter en el atributo, los genera automáticamente.

Si vamos un poco más allá, también el lenguaje Groovy hace automáticamente estas cosas, pone en los bytecodes compilados métodos getter y setter para los atributos, de forma que podemos usarlos desde otros lenguajes como java.

Maravilloso, todos sabemos que hacer los atributos privados y poner setter y getter es una de las buenas costumbres de programación y es pecado no hacerlo, así que todas estas maravillosas herramientas lo hacen AUTOMATICAMENTE por nosotros, ahorrándonos escribir el tedioso código.

Pero creo que nos olvidamos del fondo. ¿Por qué es bueno hacer los atributos privados y poner getter y setter?. La respuesta todos la sabemos: encapsulación. Pero, ¿nos hemos parado a pensar realmente qué es eso?

Imagina un atributo privado "chisme" con su correspondiente getter y setter

private double chisme;
public void setChisme(double chisme) {
   this.chisme=chisme;
}
public double getChisme() {
   return this.chisme;
}

La ventaja de la encapsulación es que si más adelante decido que en vez "chismes" tengo "trvilorios" (todos sabemos que los "chismes" y los "trivilorios" son equivalentes y podemos obtener unos de otros) y que en vez de double, es un BigDecimal, podemos hacer esto

private BigDecimal trivilorio;
public void setChisme(double chisme) {
   this.trivilorio = getTrivilorioAPartirDeChisme(chisme);
}
public double getChisme() {
   return getChismeAPartirDeTrivilorio(this.trivilorio);
}

Y esta es la verdadera ventaja, hemos cambiado el atributo, pero mantenemos los setter y getter de chisme, haciendo las cuentas necesarias en esos métodos. De esta forma, todo el código que use nuestra clase seguirá usando setChisme() y getChisme() y seguirá trabajando con chismes y no con tirvilorios, no necesitamos cambiar nada, posiblemente ni siquiera recompilar ese otro código,  y todo seguirá funcionando.

Pero si usamos maravillosas herramientas que generan automáticamente los getter y setter, perdemos el encapsulamiento, que es precisamente el único motivo de la existencia de esos setter y getter. En el momento que cambie "chisme" por "trivilorio", aparecerán automáticamente getTrivilorio() y setTrivilorio(), pero desaparecerán, también automáticamente, getChisme() y setChisme(), por lo que todo el código que hay por ahí usando setChisme() y getChisme() se debe rehacer, recompilar…. y posiblemente hacer las conversiones de chisme a trivilorio y viceversa, o bien tocar el código que usaba chismes y ahora tiene que usar trivilorios.

Así que ojo con estas herramientas. Si queremos que el código que usa nuestra clase no tenga que cambiar y usamos estas herramientas, después de cambiar chisme  por trivilorio, debemos escribir manualmente los métodos getChisme() y setChisme() con su correspondiente conversión, manteniendo así la compatibilidad y la encapsulación. Por supuesto, setTrivilorio() y getTrivilorio() pueden y deben añadirse, automática o manualmente.