Apache Groovy es un lenguaje de programación orientado a objetos compatible con la sintaxis de Java para la plataforma Java . Es un lenguaje tanto estático como dinámico con características similares a las de Python , Ruby y Smalltalk . Se puede utilizar como lenguaje de programación y lenguaje de secuencias de comandos para la plataforma Java, está compilado en el código de bytes de la máquina virtual Java (JVM) e interopera perfectamente con otros códigos y bibliotecas de Java . Groovy utiliza una sintaxis de llaves similar a la de Java. Groovy admite cierres , cadenas multilínea y expresiones incrustadas en cadenas . Gran parte del poder de Groovy reside en sus transformaciones AST , activadas mediante 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 la última versión importante bajo el patrocinio de Pivotal Software , que finalizó en marzo de 2015. [6] Desde entonces, Groovy ha cambiado su estructura de gobierno 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 presentado al JCP como JSR 241 [9] y aceptado mediante votación. Se lanzaron varias versiones entre 2004 y 2006. Después de que comenzó el esfuerzo de estandarización del Java Community Process (JCP), la numeración de las versiones cambió y el 2 de enero de 2007 se lanzó una versión llamada "1.0". Después de varias versiones beta y candidatas a la versión 1.1, El 7 de diciembre de 2007, se lanzó Groovy 1.1 Final e inmediatamente pasó a ser Groovy 1.5 para reflejar los numerosos 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 líder de especificaciones cambió el estado de 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 necesaria ] En octubre de 2016, Strachan declaró: "Todavía amo groovy (¡las tuberías de jenkins son tan geniales!), java, go, mecanografiado y kotlin". [14]
El 2 de julio de 2012 se lanzó Groovy 2.0, que, entre otras características nuevas, agregó compilación estática y verificación de tipos estáticos .
Cuando EMC Corporation (EMC) y VMware escindieron la empresa conjunta Pivotal Software en abril de 2013, Groovy y Grails formaron 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 Codehaus a un Comité de Gestión de Proyectos (PMC) en 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 de Java aprendan Groovy gradualmente comenzando con la sintaxis familiar de Java antes de adquirir más modismos de programación de Groovy . [19]
Las características de Groovy que no están disponibles en Java incluyen escritura estática y dinámica (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 la operador de navegación segura ?.
para comprobar automáticamente si hay punteros nulos (por ejemplo, variable?.method()
o variable?.field
). [20]
Desde la versión 2, Groovy también admite modularidad (pudiendo enviar solo los frascos 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 continuas de rendimiento utilizando el invokedynamic
Instrucción introducida en Java 7 . [21]
Groovy proporciona soporte nativo para varios lenguajes de marcado como XML y HTML , logrado a través de 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 necesaria ]
A diferencia de Java, un archivo de código fuente de Groovy se puede ejecutar como un script (no compilado) , 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 Groovy se analiza, compila y genera completamente antes de ejecutarlo (similar a Python y Ruby). Esto ocurre internamente y la versión compilada no se guarda como un artefacto del proceso. [22]
GroovyBeans es la versión de Groovy de JavaBeans . Groovy genera implícitamente captadores y definidores. En el siguiente código, setColor(String color)
y getColor()
se generan implícitamente. Las dos últimas líneas, que parecen acceder al color directamente, en realidad llaman a los métodos generados implícitamente. [23]
clase AGroovyBean { color de cadena } def myGroovyBean = nuevo AGroovyBean () miGroovyBean . setColor ( 'azul bebé' ) afirma myGroovyBean . getColor () == 'azul bebe' miGroovyBean . color = 'peltre' afirmar myGroovyBean . color == 'peltre'
Groovy ofrece una sintaxis simple y consistente para manejar listas y mapas , que recuerda a la sintaxis de matrices de Java . [24]
def movieList = [ 'Dersu Uzala' , 'Ran' , 'Seven Samurai' ] // Parece una matriz, pero es una lista afirmar movieList [ 2 ] == 'Seven Samurai' movieList [ 3 ] = 'Casablanca' // Agrega un elemento a la lista afirmar movieList . tamaño () == 4 def MonthMap = [ 'Enero' : 31 , 'Febrero' : 28 , 'Marzo' : 31 ] // Declara un mapa afirmar MonthMap [ 'Marzo' ] == 31 // Accede a una entrada MonthMap [ 'Abril' ] = 30 // Agrega una entrada al mapa afirmar MonthMap . tamaño () == 4
Groovy ofrece soporte para 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 fácilmente los cambios en la clase, similar al concepto de clase abierta de Ruby :
Número . metaClass { sqrt = { Matemáticas . sqrt ( delegado ) } } afirmar 9 . sqrt () == 3 afirmar 4 . raíz cuadrada () == 2
Los cambios de Groovy en el código a través de la creación de prototipos no son visibles en Java, ya que cada invocación de atributo/método en Groovy pasa por el registro de metaclase. Solo se puede acceder al código modificado desde Java yendo al registro de metaclase.
Groovy también permite anular métodos como getProperty()
, propertyMissing()
entre otros, permitir al desarrollador interceptar llamadas a un objeto y especificar una acción para ellos, de una manera simplificada orientada a aspectos . El siguiente código permite que la clase java.lang.String
responda a la hex
propiedad:
enum Color { NEGRO ( '#000000' ), BLANCO ( '#FFFFFF' ), ROJO ( '#FF0000' ), AZUL ( '#0000FF' ) Cadena hexadecimal Color ( Cadena hexadecimal ) { this . hexadecimal = hexadecimal } } Cadena . metaclase . getProperty = { Propiedad de cadena -> def stringColor = delegar if ( propiedad == 'hex' ) { Color . valores (). Encuéntralo . nombre (). igual aIgnoreCase stringColor }?. hexadecimal } } afirmar "BLANCO" . hexadecimal == "#FFFFFF" afirmar "AZUL" . hexadecimal == "#0000FF" afirmar "NEGRO" . hexadecimal == "#000000" afirmar "VERDE" . hexadecimal == nulo
El marco Grails utiliza ampliamente la metaprogramación para habilitar buscadores dinámicos GORMUser.findByName('Josh')
, como otros. [26]
La sintaxis de Groovy permite omitir paréntesis y puntos en algunas situaciones. El siguiente código maravilloso
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 de dominio específico (DSL) que parecen inglés simple.
Aunque Groovy es principalmente un lenguaje orientado a objetos, también ofrece funciones de programación funcionales .
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 momento posterior". [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, implícita, escrita y parámetros sin tipo.
Al trabajar sobre 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 % 2 == 0 (par), es falso. * El IDE puede inferir el tipo del parámetro implícito "it" como un número entero. * También podría escribirse como: * list.findAll { Entero i -> i % 2 } * list.findAll { i -> i % 2 } */ def probabilidades = lista . encontrarTodo { es % 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 de respuesta se puede asignar en un momento posterior mediante delegación:
// Este bloque de código contiene expresiones sin referencia a una implementación def operaciones = { declarar 5 suma 4 dividir 3 imprimir }
/* * 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 utilizando, por ejemplo, operaciones de servicio web * en los cálculos. */ clase Expresión { Valor decimal grande /* * Aunque se pasa un número entero como parámetro, se convierte en un BigDecimal, como * se definió. Si la clase tuviera un método 'declarar (valor entero)', se usaría en su lugar. */ def declarar ( valor BigDecimal ) { this . valor = valor } def suma ( valor BigDecimal para agregar ) { this . valor += valorAgregar } def dividir ( divisor BigDecimal ) { this . valor /= divisor } def propiedadMissing ( propiedad de cadena ) { if ( propiedad == "imprimir" ) println valor } }
// Aquí se define quién va a responder las expresiones en el bloque de código anterior. operaciones . delegado = nueva expresión () operaciones ()
Generalmente llamada aplicación parcial , [28] esta característica de Groovy 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 vinculado. Proporcionar un argumento al curry()
método solucionará el argumento uno. Proporcionar N argumentos arreglará los argumentos 1 .. N.
def joinTwoWordsWithSymbol = { símbolo , primero , segundo -> primero + símbolo + segundo } afirmar joinTwoWordsWithSymbol ( '#' , 'Hola' , 'Mundo' ) == 'Hola#Mundo' def concatWords = unirTwoWordsWithSymbol . curry ( '' ) afirmar concatWords ( 'Hola' , 'Mundo' ) == 'Hola mundo' def anteponerHola = concatWords . curry ( 'Hola' ) //def prependHello = joinTwoWordsWithSymbol.curry(' ', 'Hola') afirmar prependHello ( 'Mundo' ) == 'Hola mundo'
Curry también se puede usar 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 . correr ( 3 ) afirmar poder ( 2 , 2 ) == 4 afirmar cuadrado ( 4 ) == 16 afirmar cubo ( 3 ) == 27
Groovy también admite evaluación diferida , [29] [30] reducción/plegado , [31] estructuras infinitas e inmutabilidad , [32] entre otros. [33]
En notación de objetos JavaScript ( JSON ) y procesamiento XML, 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:
<idiomas> <idioma año= "1995" > <nombre> Java </nombre> <paradigma> orientado a objetos </paradigma> <tipificación > estático </ tipificación> </idioma> <idioma año= "1995" > <nombre > Ruby </name> <paradigm> funcional, orientado a objetos </paradigm> <typing> tipificación pato , dinámica </typing> </language> <language año= "2003" > <name> Groovy </name> <paradigm > funcional, orientado a objetos </paradigm> <typing> tipificación pato , dinámico, estático </typing> </language> </languages>
se puede generar a través del siguiente código Groovy:
def escritor = nuevo StringWriter () def constructor = nuevo maravilloso . xml . Constructor MarkupBuilder ( escritor ) . idiomas { idioma ( año: 1995 ) { nombre paradigma "Java" "orientado a objetos" escritura "estática" } idioma ( año: 1995 ) { nombre paradigma "Ruby" escritura "funcional, orientada a objetos" " escritura pato, dinámica" } idioma ( año: 2003 ) { nombre Paradigma "maravilloso" escritura "funcional, orientada a objetos" " escritura pato, dinámica, estática" } }
y también se puede procesar en forma de streaming 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 . Encadenar () // Aquí se emplea la sintaxis de expresiones regulares de Groovy para un comparador (=~) que será forzado a // un valor booleano: ya sea verdadero, si el valor contiene nuestra cadena, o falso en caso contrario. def funcional = idiomas . idioma . encontrarTodo { eso . paradigma =~ "funcional" } afirmar funcional . recogerlo . nombre } == [ "Maravilloso" , "Rubí" ]
En Groovy, las cadenas se pueden interpolar con variables y expresiones usando GStrings: [35]
BigDecimal account = 10.0 def text = "La cuenta muestra actualmente un saldo de $cuenta" afirmar texto == "La cuenta muestra actualmente un saldo de 10.0"
Las cadenas G que contienen variables y expresiones deben declararse utilizando comillas dobles.
Una expresión compleja debe estar entre llaves. Esto evita que partes del mismo 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}" afirmar texto == "La cuenta muestra actualmente un saldo de 6.0" // Sin los corchetes para aislar la expresión, esto resultaría: text = "La cuenta muestra actualmente un saldo de $cuenta - menos" afirmar texto == "La cuenta muestra actualmente un saldo de 10,0 - menos"
La evaluación de expresiones se puede aplazar empleando la sintaxis de flechas:
BigDecimal tax = 0.15 text = "La cuenta muestra actualmente un saldo de ${->account - account*tax}" tax = 0.10 // El valor del impuesto se cambió DESPUÉS de la declaración de GString. La expresión // variables se vinculan sólo cuando la expresión realmente debe evaluarse: afirmar texto == "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 momento del proceso, el código fuente terminará representándose en la memoria en forma de un árbol de sintaxis concreta, luego transformado en un árbol de sintaxis abstracta. El propósito de AST Transformations es permitir a los desarrolladores conectarse 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. AST Transformations proporciona a Groovy capacidades mejoradas de metaprogramación en tiempo de compilación, lo que permite una gran flexibilidad. a nivel de lenguaje, sin penalización en el 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 luego se manipula en AST para obtener un código válido. [37] Un ejemplo de tal prueba es:
def "el máximo de #a y #b es #c" () { esperar: Matemáticas . máximo ( a , b ) == c donde: un | segundo || c 3 | 5 || 5 7 | 0 || 7 0 | 0 || 0 }
Según la documentación de Groovy, " los rasgos son una construcción estructural del lenguaje que permite: composición de comportamientos, implementación de interfaces en tiempo de ejecución, anulación de comportamientos y compatibilidad con verificación/compilación de tipos estáticos".
Los rasgos pueden verse como interfaces que llevan tanto implementaciones como estados predeterminados. Un rasgo se define utilizando la palabra clave de 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 usar como una interfaz normal usando la palabra clave implements
:
class Bird implementa FlyingAbility {} /* Agrega el rasgo FlyingAbility a las capacidades de la clase Bird */ def bird = new Bird () /* crea una instancia de un nuevo Bird */ afirmar bird . volar () == "¡Estoy volando!" /* la clase Bird obtiene automáticamente el comportamiento del rasgo FlyingAbility */
Los rasgos permiten una amplia gama de habilidades, desde una simple composición hasta pruebas.
Ejemplos notables de adopción de Groovy incluyen:
Muchos entornos de desarrollo integrados (IDE) y editores de texto admiten Groovy:
Existe una implementación alternativa de Groovy: