Apache Groovy es un lenguaje de programación orientado a objetos compatible con la sintaxis de Java para la plataforma Java . Es un lenguaje estático y dinámico con características similares a las de Python , Ruby y Smalltalk . Se puede utilizar como lenguaje de programación y como lenguaje de script para la plataforma Java, se compila en código de bytes de máquina virtual Java (JVM) e interopera sin problemas con otros códigos y bibliotecas de Java . Groovy utiliza una sintaxis de llaves similar a la de Java. Groovy admite cierres , cadenas de varias líneas y expresiones incrustadas en cadenas . Gran parte del poder de Groovy reside en sus transformaciones AST , activadas a través de anotaciones.
Groovy 1.0 se lanzó el 2 de enero de 2007 y Groovy 2.0 en julio de 2012. Desde la versión 2, Groovy se puede compilar estáticamente , ofreciendo inferencia de tipos y rendimiento cercano al de Java. [4] [5] Groovy 2.4 fue el último lanzamiento importante bajo el patrocinio de Pivotal Software que finalizó en marzo de 2015. [6] Desde entonces, Groovy ha cambiado su estructura de gobernanza a un Comité de Gestión de Proyectos en la Apache Software Foundation . [7]
James Strachan habló por primera vez sobre el desarrollo de Groovy en su blog en agosto de 2003. [8] En marzo de 2004, Groovy fue enviado al JCP como JSR 241 [9] y aceptado por votación. Se publicaron varias versiones entre 2004 y 2006. Después de que comenzara el esfuerzo de estandarización del Java Community Process (JCP), la numeración de las versiones cambió y se publicó una versión llamada "1.0" el 2 de enero de 2007. Después de varias versiones beta y candidatas a lanzamiento numeradas 1.1, el 7 de diciembre de 2007 se publicó Groovy 1.1 Final y se renumeró inmediatamente como Groovy 1.5 para reflejar los muchos cambios realizados.
En 2007, Groovy ganó el primer premio en el premio a la innovación JAX 2007. [10] En 2008, Grails , un marco web Groovy , ganó el segundo premio en el premio a la innovación JAX 2008. [11]
En noviembre de 2008, SpringSource adquirió la empresa Groovy and Grails (G2One). [12] En agosto de 2009, VMware adquirió SpringSource. [13]
En abril de 2012, después de ocho años de inactividad, el jefe de especificaciones cambió el estado del JSR 241 a inactivo. [9]
Strachan había abandonado el proyecto en silencio un año antes del lanzamiento de Groovy 1.0 en 2007. [ cita requerida ] En octubre de 2016, Strachan declaró: "Todavía me encanta Groovy (¡las canalizaciones de Jenkins son tan maravillosas!), Java, Go, Typescript y Kotlin". [14]
El 2 de julio de 2012 se lanzó Groovy 2.0, que, entre otras novedades, añadió compilación estática y comprobación de tipos estática .
Cuando la empresa conjunta Pivotal Software fue escindida por EMC Corporation (EMC) y VMware en abril de 2013, Groovy y Grails pasaron a formar parte de su cartera de productos. Pivotal dejó de patrocinar a Groovy y Grails a partir de abril de 2015. [6] Ese mismo mes, Groovy cambió su estructura de gobierno de un repositorio de Codehaus a un Comité de Gestión de Proyectos (PMC) en la Apache Software Foundation a través de su incubadora. [7] Groovy se graduó de la incubadora de Apache y se convirtió en un proyecto de alto nivel en noviembre de 2015. [15]
El 7 de febrero de 2020 se lanzó Groovy 3.0. [16] La versión 4.0 se lanzó el 25 de enero de 2022. [17]
La mayoría de los archivos Java válidos también son archivos Groovy válidos. Aunque los dos lenguajes son similares, el código Groovy puede ser más compacto, porque no necesita todos los elementos que necesita Java. [18] Esto hace posible que los programadores Java aprendan Groovy gradualmente comenzando con la sintaxis Java familiar antes de adquirir más modismos de programación Groovy . [19]
Las características de Groovy que no están disponibles en Java incluyen tipado estático y dinámico (con la palabra clave def
), sobrecarga de operadores , sintaxis nativa para listas y matrices asociativas (mapas), soporte nativo para expresiones regulares , iteración polimórfica, interpolación de cadenas , métodos auxiliares agregados y el operador de navegación segura ?.
para verificar automáticamente punteros nulos (por ejemplo, variable?.method()
, o variable?.field
). [20]
Desde la versión 2, Groovy también admite modularidad (poder enviar solo los jar necesarios según las necesidades del proyecto, reduciendo así el tamaño de la biblioteca de Groovy), verificación de tipos, compilación estática, mejoras de sintaxis de Project Coin, bloques multicatch y mejoras de rendimiento continuas utilizando la invokedynamic
instrucción introducida en Java 7. [ 21]
Groovy ofrece compatibilidad nativa con varios lenguajes de marcado, como XML y HTML , mediante una sintaxis de modelo de objetos de documento (DOM) en línea. Esta característica permite la definición y manipulación de muchos tipos de activos de datos heterogéneos con una sintaxis y una metodología de programación uniformes y concisas. [ cita requerida ]
A diferencia de Java, un archivo de código fuente de Groovy se puede ejecutar como un script (sin compilar) , si contiene código fuera de cualquier definición de clase, si es una clase con un método principal o si es un Runnable o GroovyTestCase . Un script de Groovy se analiza, compila y genera por completo antes de ejecutarse (de manera similar a Python y Ruby). Esto ocurre en segundo plano y la versión compilada no se guarda como un artefacto del proceso. [22]
Los GroovyBeans son la versión de Groovy de los JavaBeans . Groovy genera métodos get y setter de forma implícita. En el código siguiente, setColor(String color)
y getColor()
se generan de forma implícita. Las dos últimas líneas, que parecen acceder directamente al color, en realidad están llamando a los métodos generados de forma implícita. [23]
clase AGroovyBean { Cadena color } def myGroovyBean = nuevo AGroovyBean () myGroovyBean . setColor ( 'bebé azul' ) assert myGroovyBean . getColor () == 'bebé azul' myGroovyBean . color = 'peltre' assert myGroovyBean . color == 'peltre'
Groovy ofrece una sintaxis simple y consistente para manejar listas y mapas , que recuerda a la sintaxis de matriz de Java . [24]
def movieList = [ 'Dersu Uzala' , 'Ran' , 'Seven Samurai' ] // Parece una matriz, pero es una lista assert movieList [ 2 ] == 'Seven Samurai' movieList [ 3 ] = 'Casablanca' // Agrega un elemento a la lista assert movieList . size () == 4 def monthMap = [ 'January' : 31 , 'February' : 28 , 'March' : 31 ] // Declara un mapa assert monthMap [ 'March' ] == 31 // Accede a una entrada monthMap [ 'April' ] = 30 // Agrega una entrada al mapa assert monthMap . size () == 4
Groovy ofrece soporte para la extensión de prototipos a través de , módulos de extensión (solo en Groovy 2), categoríasExpandoMetaClass
similares a Objective-C y . [25]DelegatingMetaClass
ExpandoMetaClass
ofrece un lenguaje específico de dominio (DSL) para expresar los cambios en la clase fácilmente, similar al concepto de clase abierta de Ruby :
Número . metaClass { sqrt = { Math . sqrt ( delegado ) } } afirmar 9 . sqrt () == 3 afirmar 4 . raíz cuadrada () == 2
Los cambios en el código de Groovy a través de la creación de prototipos no son visibles en Java, ya que cada invocación de atributo o método en Groovy pasa por el registro de metaclases. Solo se puede acceder al código modificado desde Java yendo al registro de metaclases.
Groovy también permite anular métodos como getProperty()
, propertyMissing()
entre otros, lo que permite al desarrollador interceptar llamadas a un objeto y especificar una acción para ellas, de una manera simplificada y orientada a aspectos . El siguiente código permite que la clase java.lang.String
responda a la hex
propiedad:
enumeración Color { NEGRO ( '#000000' ), BLANCO ( '#FFFFFF' ), ROJO ( '#FF0000' ), AZUL ( '#0000FF' ) Cadena hexadecimal Color ( Cadena hexadecimal ) { this . hex = hex } } Cadena . metaClass . getProperty = { Cadena propiedad -> def stringColor = delegate if ( propiedad == 'hex' ) { Color . values (). find { it . name (). equalsIgnoreCase stringColor }?. hex } } afirmar "BLANCO" . hex == "#FFFFFF" afirmar "AZUL" . hex == "#0000FF" afirmar "NEGRO" . hex == "#000000" afirmar "VERDE" . hex == null
El marco Grails utiliza ampliamente la metaprogramación para permitir buscadores dinámicos GORMUser.findByName('Josh')
, como y otros. [26]
La sintaxis de Groovy permite omitir los paréntesis y los puntos en algunas situaciones. El siguiente código de Groovy
tomar ( café ) con ( azúcar , leche ) y ( licor )
se puede escribir como
tomar café con azúcar , leche y licor
permitiendo el desarrollo de lenguajes específicos de dominio (DSL) que parecen inglés simple.
Aunque Groovy es principalmente un lenguaje orientado a objetos, también ofrece características de programación funcional .
Según la documentación de Groovy: "Los cierres en Groovy funcionan de manera similar a un 'puntero de método', lo que permite escribir y ejecutar código en un punto posterior en el tiempo". [27] Los cierres de Groovy admiten variables libres, es decir, variables que no se le han pasado explícitamente como parámetro, pero que existen en su contexto de declaración, aplicación parcial (que denomina ' currying ' [28] ), delegación, parámetros implícitos, tipados y no tipados.
Cuando se trabaja con colecciones de un tipo determinado, se puede inferir el cierre pasado a una operación sobre la colección:
lista = [ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 ] /* * Los números distintos de cero se convierten en verdaderos, por lo que cuando it % 2 == 0 (par), es falso. * El IDE puede inferir el tipo del parámetro "it" implícito como un entero. * También se puede escribir como: * list.findAll { Integer i -> i % 2 } * list.findAll { i -> i % 2 } */ def odds = list . findAll { it % 2 } afirmar probabilidades == [ 1 , 3 , 5 , 7 , 9 ]
Se puede escribir un grupo de expresiones en un bloque de cierre sin referencia a una implementación y el objeto que responde se puede asignar en un punto posterior mediante delegación:
// Este bloque de código contiene expresiones sin referencia a una implementación def operaciones = { declare 5 sum 4 divide 3 print }
/* * Esta clase manejará las operaciones que se pueden usar en el cierre anterior. Se podría declarar otra clase * que tenga los mismos métodos, pero que use, por ejemplo, operaciones de servicio web * en los cálculos. */ class Expression { BigDecimal value /* * Aunque se pasa un entero como parámetro, se convierte en un BigDecimal, como se definió. Si la clase tuviera un método 'declare(Integer value)', se utilizaría en su lugar. */ def declare ( BigDecimal value ) { this . value = value } def sum ( BigDecimal valueToAdd ) { this . value += valueToAdd } def divide ( BigDecimal divisor ) { this . value /= divisor } def propertyMissing ( String property ) { if ( property == "print" ) println value } }
//Aquí se define quién va a responder las expresiones en el bloque de código anterior. operaciones . delegado = new Expresión () operaciones ()
Esta característica de Groovy, que suele denominarse aplicación parcial , [28] permite que los parámetros de los cierres se establezcan en un parámetro predeterminado en cualquiera de sus argumentos, creando un nuevo cierre con el valor límite. Si se proporciona un argumento al curry()
método, se solucionará el argumento uno. Si se proporcionan N argumentos, se solucionarán los argumentos 1...N.
def joinTwoWordsWithSymbol = { símbolo , primero , segundo -> primero + símbolo + segundo } assert joinTwoWordsWithSymbol ( '#' , 'Hola' , 'Mundo' ) == 'Hola#Mundo' def concatWords = joinTwoWordsWithSymbol . curry ( ' ' ) assert concatWords ( 'Hola' , 'Mundo' ) == 'Hola Mundo' def prependHello = concatWords . curry ( 'Hola' ) //def prependHello = joinTwoWordsWithSymbol. curry(' ', 'Hola') assert prependHello ( 'Mundo' ) == 'Hola Mundo'
Curry también se puede utilizar en la dirección inversa (arreglando los últimos N argumentos) usando rcurry()
.
def potencia = { valor BigDecimal , potencia BigDecimal -> valor ** potencia } def cuadrado = potencia . rcurry ( 2 ) def cubo = potencia . rcurry ( 3 ) afirmar potencia ( 2 , 2 ) == 4 afirmar cuadrado ( 4 ) == 16 afirmar cubo ( 3 ) == 27
Groovy también admite evaluación perezosa , [29] [30] reducción/plegado , [31] estructuras infinitas e inmutabilidad , [32] entre otras. [33]
En el procesamiento de XML y la notación de objetos de JavaScript ( JSON ), Groovy emplea el patrón Builder , lo que hace que la producción de la estructura de datos sea menos detallada. Por ejemplo, el siguiente XML:
<languages> <language year= "1995" > <name> Java </name> <paradigm> orientado a objetos </paradigm> <typing> estático </typing> </language> < language year = "1995" > <name> Ruby </name> <paradigm> funcional, orientado a objetos </paradigm> <typing> tipado de pato , dinámico </typing> </language> <language year= "2003" > <name> Groovy </name> <paradigm> funcional, orientado a objetos </paradigm> <typing> tipado de pato , dinámico, estático </typing> </language> </languages>
se puede generar mediante el siguiente código Groovy:
def writer = new StringWriter () def builder = new groovy.xml.MarkupBuilder ( writer ) builder.languages { language ( year: 1995 ) { name "Java" paradigma "orientado a objetos" tipificación " estática " } language ( year : 1995 ) { name " Ruby" paradigma " funcional, orientado a objetos" tipificación "tipificación dinámica" } language ( year: 2003 ) { name "Groovy" paradigma "funcional, orientado a objetos" tipificación " tipificación dinámica, estática" } }
y también se puede procesar de forma continua a través de StreamingMarkupBuilder
. Para cambiar la implementación a JSON, se MarkupBuilder
puede cambiar a JsonBuilder
. [34]
Para analizarlo y buscar un lenguaje funcional, findAll
el método de Groovy puede servir:
def idiomas = nuevo XmlSlurper () . parseText escritor.toString ( ) // Aquí se emplea la sintaxis de expresiones regulares de Groovy para un comparador (=~) que se convertirá a un // valor booleano: verdadero, si el valor contiene nuestra cadena, o falso en caso contrario. def functional = languages . language . findAll { it . paradigm =~ "functional" } assert functional . collect { it . name } == [ "Groovy" , "Ruby" ]
En Groovy, las cadenas se pueden interpolar con variables y expresiones mediante GStrings: [35]
Cuenta BigDecimal = 10.0 def text = "La cuenta muestra actualmente un saldo de $cuenta" assert text == "La cuenta muestra actualmente un saldo de 10.0"
Las GStrings que contienen variables y expresiones deben declararse mediante comillas dobles.
Una expresión compleja debe estar entre llaves. Esto evita que partes de ella se interpreten como pertenecientes a la cadena circundante en lugar de a la expresión:
BigDecimal minus = 4.0 text = "La cuenta muestra actualmente un saldo de ${account - minus}" assert text == "La cuenta muestra actualmente un saldo de 6.0" // Sin los corchetes para aislar la expresión, esto daría como resultado: text = "La cuenta muestra actualmente un saldo de $cuenta - menos" assert text == "La cuenta muestra actualmente un saldo de 10.0 - menos"
La evaluación de expresiones se puede diferir empleando la sintaxis de flecha:
BigDecimal tax = 0.15 text = "La cuenta muestra actualmente un saldo de ${->account - account*tax}" tax = 0.10 // El valor del impuesto se modificó DESPUÉS de la declaración de GString. Las variables de expresión se vinculan solo cuando la expresión debe evaluarse realmente: assert text == "La cuenta muestra actualmente un saldo de 9.000"
Según la propia documentación de Groovy, "Cuando el compilador de Groovy compila scripts y clases de Groovy, en algún punto del proceso, el código fuente terminará siendo representado en la memoria en forma de un árbol de sintaxis concreta, y luego se transformará en un árbol de sintaxis abstracta. El propósito de las transformaciones AST es permitir que los desarrolladores se conecten al proceso de compilación para poder modificar el AST antes de que se convierta en código de bytes que será ejecutado por la JVM. Las transformaciones AST proporcionan a Groovy capacidades mejoradas de metaprogramación en tiempo de compilación que permiten una gran flexibilidad a nivel de lenguaje, sin una penalización del rendimiento en tiempo de ejecución". [36]
Ejemplos de AST en Groovy son:
Entre otros.
El marco de pruebas Spock utiliza transformaciones AST para permitir al programador escribir pruebas en una sintaxis no compatible con Groovy, pero el código relevante se manipula luego en el AST para convertirlo en código válido. [37] Un ejemplo de dicha prueba es:
def "el máximo de #a y #b es #c" () { esperar: Math . max ( a , b ) == c donde : a | b || c 3 || 5 || 5 7 || 0 || 7 0 || 0 }
Según la documentación de Groovy, " Los rasgos son una construcción estructural del lenguaje que permite: la composición de comportamientos, la implementación de interfaces en tiempo de ejecución, la anulación de comportamientos y la compatibilidad con la compilación/compilación de tipos estáticos".
Los rasgos pueden considerarse interfaces que contienen tanto implementaciones predeterminadas como estados. Un rasgo se define utilizando la palabra clave rasgo:
rasgo FlyingAbility { /* declaración de un rasgo */ String fly () { "¡Estoy volando!" } /* declaración de un método dentro de un rasgo */ }
Luego, se puede utilizar como una interfaz normal usando la palabra clave implements
:
clase Bird implementa FlyingAbility {} /* Agrega el rasgo FlyingAbility a las capacidades de la clase Bird */ def bird = new Bird () /* instancia un nuevo Bird */ assert bird . fly () == "¡Estoy volando!" /* la clase Bird obtiene automáticamente el comportamiento del rasgo FlyingAbility */
Los rasgos permiten una amplia gama de habilidades, desde la composición simple hasta la prueba.
Algunos ejemplos notables de adopción de Groovy incluyen:
Muchos entornos de desarrollo integrados (IDE) y editores de texto admiten Groovy:
Hay una implementación alternativa de Groovy: