¿Y por qué querríamos mejorar nuestro código Java? Seguro que en más de una ocasión escribiendo Java has pensado que es muy verboso o que seguro que tiene que haber una forma más fácil y sencilla de hacer esto. En ese caso, Groovy es para ti.
Apache Groovy es un lenguaje dinámico, opcionalmente tipado, con posibilidad de tipado y compilación estáticos para la JVM. Tiene como objetivo multiplicar la productividad de los desarrolladores gracias a una sintaxis concisa, familiar y fácil de aprender. Se integra sin problemas con cualquier programa Java e inmediatamente proporciona a una aplicación poderosas características como scripting, DSLs, metaprogramación en runtime y compile-time y programación funcional.
Características más importantes
Entre las características más importantes de Groovy, destacan:
Tipado dinámico: Si sabemos el tipo de una variable podemos definirlo como en Java, pero en ocasiones, para variables locales de métodos el código es más legible si no lo hacemos. En ese caso usaremos la palabra reservada
def
como tipo.Compilación estática opcional: Desde la versión 2.0 (hace más de 4 años) Groovy soporta compilación estática, es decir, el bytecode generado es muy similar al que generaría el código Java equivalente. Con esta característica Groovy puede llegar a ser tan rápido y eficiente como Java a costa de sacrificar su dinamismo, que, siendo realistas, no necesitaremos en el 90% de nuestro código.
Perfecta integración con Java: Desde Groovy podemos utilizar cualquier biblioteca Java existente en la actualidad sin necesidad de adaptarla ni de hacer nada especial. Simplemente añadimos la dependencia al classpath y listo, ya podemos utilizarla desde Groovy.
Sintaxis nativa de listas, mapas y rangos: Groovy propocina una sintaxis nativa y muy explícita para listas, mapas y rangos con la que podemos conseguir que nuestro código sea muy legible y conciso.
Las excepciones son unchecked: Se han escrito ríos de tinta sobre las checked exceptions de Java pero la gran mayoría de los defensores del lenguaje y Java Champions coinciden en que ha sido uno de los errores en el diseño del lenguaje. En Groovy todas son unchecked, por lo que no es necesario ensuciar de manera innecesaria la signatura de los métodos con
throws
que no van a ningún sitio ni tener bloquescatch
en blanco o con un simpleSystem.out.println
.
Diferencias con Java
Podría escribir varios artículos sobre las diferencias entre Java y Groovy, pero las más importantes son:
Uso de properties
En multitud de ocasiones definimos una clase con atributos privados y un montón de getters y setters para acceder a ellos. En Groovy no es necesario, el compilador se encarga de definirlos por nosotros.
class Persona {
String nombre
int edad
}
Con este código estamos definiendo la clase pública Persona
y los atributos privados nombre
y edad
. El compilador generará los getters y setters necesarios y podremos escribir el siguiente código que llamará al setter. También podemos escribir el código entre comentarios que sería el equivalente en Java.
persona.nombre = 'Iván' // Igual que persona.setNombre('Iván')
Adicionalmente si anotamos la clase con @Canonical
tendremos una implementación para equals
, hashCode
, toString
y tendremos constructores para utilizar los atributos definidos en la clase.
Strings interpolados (GStrings)
Creo que lo mejor es verlo con un ejemplo:
println "El precio de la acción $accion a día $fecha es $valor"
Se sustituirá el valor de las variables en el string sin necesidad de ir construyendo el string con fragmentos y variables. El código queda mucho más sencillo y fácil de leer.
Además, si en lugar de una comilla ponemos tres, podremos escribir strings multilinea:
String multilinea = """Podemos escribir
el string en
varias líneas
"""
Listas y mapas
Es muy común manipular listas y mapas en nuestro código, por lo que una sintaxis concisa hace que el código sea mucho más legible.
def lista = [1, 2, 3, 4] // ArrayList
// O también
List<Integer> lista = [1, 2, 3, 4]
def mapa = [nombre: 'Iván', edad: 36] // HashMap
println mapa.nombre // mapa.get("nombre");
mapa.edad = 37 // mapa.put("edad", 37);
Closures vs Lambdas
Java soporta Lambdas desde la versión 8 pero en Groovy tenemos el concepto de Closures desde el principio. Aunque no se comportan exactamente igual, como regla podemos decir que podremos utilizar una closure de Groovy en cualquier código que acepte una lambda de Java.
// Java
Arrays.asList(1, 2, 3).stream()
.map(i -> i * 2)
.filter(i -> i > 3)
.findFirst()
.orElseThrow(IllegalArgumentException::new);
// Groovy
[1, 2, 3].stream()
.map { i -> i * 2 }
.filter { i -> i > 3 }
.findFirst()
.orElseThrow(IllegalArgumentException.metaClass.&invokeConstructor)
El código es prácticamente el mismo y vemos que la versión Groovy, en los métodos map
y filter
usamos una Closure en lugar de la Lambda.
Operador safe navigator
¿Cuántas veces hemos tenido que proteger nuestro código Java contra NullPointerException
s. Imaginemos el siguiente código en Java:
if (order != null) {
if (order.getCustomer() != null) {
if (order.getCustomer().getAddress() != null) {
System.out.println(order.getCustomer().getAddress());
}
}
}
Cuanto menos se puede decir que es verboso y que es fácil cometer un error y finalmente tener nuestro famoso NPE. Escribamos ese código en Groovy:
println order?.customer?.address
El operador safe navigator ?.
genera prácticamente el mismo código (o bytecode) que la versión en Java. Si order
es null
, devolverá null
. Sino, evaluará si order.customer
es null
y sólo en el caso de no serlo evaluará order.customer.address
. El resultado de la ejecución del código anterior sólo podrá ser null
o la dirección del cliente, pero nunca tendremos un NullPointerException
.
Groovy dinámico
Hay ocasiones en las que el dinamismo de Groovy hace que podamos solucionar un problema de una forma muy sencilla y elegante. Imaginemos que queremos generar el siguiente JSON:
{
"speaker": {
"firstName": "Iván",
"lastName": "López",
"address": {
"city": "Madrid",
"country": "España",
"zip": 12345
},
"conferences": [
"SpringOne 2GX",
"Codemotion",
"Greach",
"JavaCro",
"..."
]
}
}
Lo único que hay que hacer es utilizar la clase JsonBuilder
y hacer una correspondencia uno a uno entre el json y el código.
import groovy.json.JsonBuilder
def builder = new JsonBuilder()
builder.
speaker {
firstName 'Iván'
lastName 'López'
address(
city: 'Madrid',
country: 'España',
zip: 12345,
)
conferences(
'SpringOne 2GX',
'Codemotion',
'Greach',
'JavaCro',
'...'
)
}
println builder.toPrettyString()
Como os podeis imaginar en la clase JsonBuilder
no existe un método speaker
y tampoco acepta los parámetros firstName
ni lastName
. Ese código se resuelve en runtime.
Closures para definir un DSL
Con Groovy podemos crear DSLs muy sencillos pero muy útiles. Imaginemos que tenemos un recurso que al utilizarlo puede lanzar una excepción y además tenemos que inicializarlo antes de usarlo y no olvidarnos de cerrarlo después de su uso. Si dejamos eso en manos del usuario de nuestra API, puede que no haga un buen uso y ocurran errores inesperados. Con Groovy podemos solucionarlo implementando el siguiente método safeResource
:
def safeResource(Closure cl) {
def resource = new ExpensiveResource()
try {
resource.open()
cl(resource)
} finally {
resource?.close()
}
}
// Y así lo usaríamos sin preocuparnos de inicializar o cerrar el recurso.
safeResource { ExpensiveResource er ->
er.writeData('Hola Genbeta Dev')
}
Syntactic sugar
Groovy añade en el Groovy Development Kit: GDK una gran cantidad de métodos y clases al JDK para hacerlo más Groovy. Conociéndolos podremos escribir nuestro código de una manera más concisa e idiomática.
Un ejemplo muy claro es leer el contenido de una URL. En Java, el código necesario sería algo parecido a lo siguiente:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
public class GetURLContent {
public static void main(String[] args) {
try {
URL url = new URL("http://www.google.com");
URLConnection conn = url.openConnection();
BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String inputLine;
while ((inputLine = br.readLine()) != null) {
System.out.println(inputLine);
}
br.close();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
¿Quereis saber cómo se escribe el código anterior en Groovy?
println 'http://www.google.com'.toURL().text
¡Y mucho más!
Me dejo muchas cosas fuera: parseo de json y xml, creación del DSLs, transformaciones AST, métodos con valores por defecto, operador elvis, power asserts (que ya vimos en el artículo sobre Spock), la Groovy truth, sintaxis nativa para expresiones regulares,... y muchos más.
Empieza sin instalar nada: Groovy web console
Has leido todo lo anterior y te apetece probar Groovy ya. La forma más rápida de probar Groovy es con la Groovy web console ya que no es necesario instalar nada, simplemente nos conectamos a esa web y podemos ejecutar de una manera sencilla y muy cómoda nuestros snippets Groovy e incluso salvarlos y compartirlos.
Instalación
Una vez que hemos decidido dar el paso e instalar Groovy, en función de nuestro sistema operativo tendremos distintas opciones. Si usamos Windows debemos descargar el instalable de la web de Groovy.
Por otro lado si utilizamos Linux o Mac, aunque también es posible descargar los binarios desde la misma web, no es la opción recomendada. En su lugar utilizaremos SDKMAN que nos permite gestionar distintas versiones de SDKs relacionados con el mundo Java: Groovy, Grails, Scala, Play, Kotlin, Spring Boot, Gradle... Y poder actualizar y cambiar de versiones de una manera sencilla y cómoda. Para instalar SDKMAN, simplemente hacemos:
$ curl -s "https://get.sdkman.io" | bash
Abrimos un nuevo terminal para que se carguen las variables de entorno correspondientes y ejecutamos:
$ sdk install groovy
Y listo, tendremos disponible la última versión de Groovy:
$ groovy -version
Ecosistema
Otro aspecto en donde brilla especialmente Groovy es en su ecosistema. Existen multitud de proyectos, herramientas y plataformas que utilizan Groovy de una manera u otra. En sucesivos artículos iremos viendo muchos de ellos con detalle pero aquí os dejo una lista: Grails, Gradle, Spock, Ratpack, SDKMAN, Geb, GPars, Griffon, Grooscript y un largo etcétera.
Conclusiones
Todo esto está muy bien, pero ¿Groovy se usa? Te sorprendería saber que hay muchas grandes empresas que utilizan Groovy entre ellas Google, Netflix o LinkedIn.
Groovy no es sólo un lenguaje de scripting igual que tampoco es lento. Groovy es la elección natural para cualquier desarrollador de la JVM que quiere ser más productivo desde el principio por su escasa curva de aprendizaje y su gran compatibilidad con Java.
Si te interesa conocer Groovy con más profundidad te recomiendo los siguientes libros Groovy in Action Second Edition, Making Java Groovy o Programming Groovy 2.
Además, si estás en Madrid o alrededores puedes asistir a las reuniones mensuales del Grupo de Usuarios de Groovy de Madrid en las que hay charlas de todas las tecnologías del ecosistema Groovy.
Si quieres darle un boost a tu proyecto Java, añade Groovy y lo tendrás.