Características ocultas de Groovy?


Parece que Groovy fue olvidado en este hilo, así que haré la misma pregunta para Groovy.

  • Intenta limitar las respuestas a Groovy core
  • Una característica por respuesta
  • Dar un ejemplo y una breve descripción de la característica, no solo un enlace a la documentación
  • Etiqueta la entidad usando el título en negrita como primera línea

Véase también:

  1. características Ocultas de Python
  2. características Ocultas de Ruby
  3. características Ocultas de Perl
  4. características Ocultas de Java
 78
Author: 10 revs, 7 users 37%anon , 2008-11-20

30 answers

Usando el operador spread-dot

def animals = ['ant', 'buffalo', 'canary', 'dog']
assert animals.size() == 4
assert animals*.size() == [3, 7, 6, 3]

Este es un atajo para animals.collect { it.size() }.

 56
Author: 2 revs, 2 users 92%Paul King,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-05-23 21:21:44

El método con permite convertir esto:

 myObj1.setValue(10)
 otherObj.setTitle(myObj1.getName())
 myObj1.setMode(Obj1.MODE_NORMAL)

En esto

 myObj1.with {
    value = 10
    otherObj.title = name
    mode = MODE_NORMAL
 }
 39
Author: Rui Vieira,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-04-30 08:52:17

Usando hashes como pseudo-objetos.

def x = [foo:1, bar:{-> println "Hello, world!"}]
x.foo
x.bar()

Combinado con duck typing, puede recorrer un largo camino con este enfoque. Ni siquiera es necesario sacar el "como" operador.

 37
Author: Robert Fischer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-11-19 22:07:37

¿Alguien sabe de Elvis?

def d = "hello";
def obj = null;

def obj2 = obj ?: d;   // sets obj2 to default
obj = "world"

def obj3 = obj ?: d;  // sets obj3 to obj (since it's non-null)
 37
Author: Bill James,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-07-22 08:52:56

Averiguar qué métodos hay en un objeto es tan fácil como preguntar a la metaClase:

"foo".metaClass.methods.name.sort().unique()

Impresiones:

["charAt", "codePointAt", "codePointBefore", "codePointCount", "compareTo",
 "compareToIgnoreCase", "concat", "contains", "contentEquals", "copyValueOf", 
 "endsWith", "equals", "equalsIgnoreCase", "format", "getBytes", "getChars", 
 "getClass", "hashCode", "indexOf", "intern", "lastIndexOf", "length", "matches", 
 "notify", "notifyAll", "offsetByCodePoints", "regionMatches", "replace", 
 "replaceAll", "replaceFirst", "split", "startsWith", "subSequence", "substring", 
 "toCharArray", "toLowerCase", "toString", "toUpperCase", "trim", "valueOf", "wait"]
 35
Author: Ted Naleid,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-06-15 17:20:56

Para interceptar los métodos estáticos que faltan, use lo siguiente

 Foo {
    static A() { println "I'm A"}

     static $static_methodMissing(String name, args) {
        println "Missing static $name"
     }
 }

Foo.A()  //prints "I'm A"
Foo.B()  //prints "Missing static B"

-Ken

 28
Author: ken,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-04-05 18:02:35

Desestructuración

Podría llamarse otra cosa en Groovy; se llama desestructuración en clojure. Nunca creerás lo útil que puede ser.

def list = [1, 'bla', false]
def (num, str, bool) = list
assert num == 1
assert str == 'bla'
assert !bool
 24
Author: Bojan Dolinar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-10-04 14:10:03

Para probar el código java con groovy, object graph builder es increíble:

def company = builder.company( name: 'ACME' ) {
   address( id: 'a1', line1: '123 Groovy Rd', zip: 12345, state: 'JV' )
   employee(  name: 'Duke', employeeId: 1 ){
      address( refId: 'a1' )
   }
}

Característica estándar, pero todavía muy agradable.

ObjectGraphBuilder

(Necesita dar cualquier propiedad de su POJO que sea List s un valor predeterminado de una lista vacía en lugar de null para que el soporte del constructor funcione.)

 21
Author: krosenvold,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-03-26 15:25:30
println 
"""
Groovy has "multi-line" strings.
Hooray!
"""
 19
Author: Brian,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-08-27 22:19:26

En groovy 1.6, las expresiones regulares funcionan con todos los iteradores de cierre (como cada uno, collect, inject, etc.) y le permiten trabajar fácilmente con los grupos de captura:

def filePaths = """
/tmp/file.txt
/usr/bin/dummy.txt
"""

assert (filePaths =~ /(.*)\/(.*)/).collect { full, path, file -> 
        "$file -> $path"
    } ==  ["file.txt -> /tmp", "dummy.txt -> /usr/bin"]
 15
Author: Ted Naleid,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-11-21 06:25:35

A diferencia de Java, en Groovy, cualquier cosa se puede usar en una instrucción switch, no solo tipos primitivos. En un método típico eventPerformed

switch(event.source) {
   case object1:
        // do something
        break
   case object2:
        // do something
        break
}
 15
Author: Rui Vieira,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-11-21 10:11:20

Usando el Operador de la Nave Espacial

Me gusta el Operador de nave espacial, útil para todo tipo de escenarios de ordenación personalizados. Algunos ejemplos de uso son aquí. Una situación en la que es particularmente útil es en la creación de un comparador sobre la marcha de un objeto utilizando múltiples campos. por ejemplo,

def list = [
    [ id:0, first: 'Michael', last: 'Smith', age: 23 ],
    [ id:1, first: 'John', last: 'Smith', age: 30 ],
    [ id:2, first: 'Michael', last: 'Smith', age: 15 ],    
    [ id:3, first: 'Michael', last: 'Jones', age: 15 ],   
]

// sort list by last name, then first name, then by descending age
assert (list.sort { a,b -> a.last <=> b.last ?: a.first <=> b.first ?: b.age <=> a.age })*.id == [ 3,1,0,2 ]
 15
Author: mmigdol,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-08-09 17:19:09

Los cierres pueden hacer que todos los viejos juegos de try-finally de gestión de recursos desaparezcan. El flujo de archivos se cierra automáticamente al final del bloque:

new File("/etc/profile").withReader { r ->
    System.out << r
}
 14
Author: John Flinchbaugh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-11-20 18:56:02

Las características proporcionadas por las transformaciones dentro del paquete groovy.transform del GDK, tales como:

  • @Immutable: La anotación @ Inmutable indica al compilador que ejecute una transformación AST que agregue los getters, constructores, iguales, hashCode y otros métodos auxiliares necesarios que se escriben típicamente al crear clases inmutables con las propiedades definidas.
  • @CompileStatic: Esto permitirá que el compilador Groovy use comprobaciones de tiempo de compilación al estilo de Java a continuación, realice la compilación estática, evitando así el protocolo Groovy meta object.
  • @Canonical: La anotación @ Canonical instruye al compilador a ejecutar una transformación AST que agrega constructores posicionales, equals, hashCode y una bonita impresión toString a su clase.

Otros:

  • @Slf4j Esta transformación local agrega una capacidad de registro a su programa usando LogBack logging. Cada llamada de método en una variable no ligada llamada log será asignado a una llamada al registrador.
  • Groovy's XML Slurper: fácil análisis de XML. Killer feature!
 13
Author: Hans Westerbeek,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-06-28 17:38:42

Puede convertir una lista en un mapa utilizando toSpreadMap(), conveniente en momentos en que el orden en la lista es suficiente para determinar las claves y los valores asociados con ellas. Vea el ejemplo a continuación.

def list = ['key', 'value', 'foo', 'bar'] as Object[]
def map = list.toSpreadMap()

assert 2 == map.size()
assert 'value' == map.key
assert 'bar' == map['foo']
 12
Author: ken,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-09-17 21:32:46

Implementación de Interfaz Basada en el Cierre

Si tiene una referencia escrita como:

MyInterface foo

Puede implementar toda la interfaz usando:

foo = {Object[] args -> println "This closure will be called by ALL methods"} as MyInterface

Alternativamente, si desea implementar cada método por separado, puede usar:

foo = [bar: {-> println "bar invoked"}, 
    baz: {param1 -> println "baz invoked with param $param1"}] as MyInterface
 12
Author: Don,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-01-07 08:52:38

Eliminar null valores de la lista

def list = [obj1, obj2, null, obj4, null, obj6]
list -= null
assert list == [obj1, obj2, obj4, obj6]
 8
Author: BIdesi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-01-16 12:05:34

Sé que I'am un poco tarde, pero creo que hay algunas características agradables que faltan aquí:

Operadores de colección más / menos

def l = [1, 2, 3] + [4, 5, 6] - [2, 5] - 3 + (7..9)
assert l == [1, 4, 6, 7, 8, 9]

def m = [a: 1, b: 2] + [c: 3] - [a: 1]
assert m == [b: 2, c: 3]

Sentencia Switch

switch (42) {
  case 0: .. break
  case 1..9: .. break
  case Float: .. break
  case { it % 4 == 0 }: .. break
  case ~/\d+/: .. break
}

Rangos e indexación

assert (1..10).step(2) == [1, 3, 5, 7, 9]
assert (1..10)[1, 4..8] == [2, 5, 6, 7, 8, 9]
assert ('a'..'g')[-4..-2] == ['d', 'e', 'f']

Nombres de variables Unicode

def α = 123
def β = 456
def Ω = α * β
assert Ω == 56088
 7
Author: micha,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-01-15 20:03:13

@Delegate

class Foo {
    def footest() { return "footest"}   
}

class Bar {
    @Delegate Foo foo = new Foo()     
}

def bar = new Bar()

assert "footest" == bar.footest()
 7
Author: Omnipresent,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-03-26 15:29:36

Subrayado en literales

Al escribir números literales largos, es más difícil a la vista averiguar cómo se agrupan algunos números, por ejemplo, con grupos de miles, de palabras, etc. Al permitirle colocar guiones bajos en literales numéricos, es más fácil detectar esos grupos:

long creditCardNumber = 1234_5678_9012_3456L
long socialSecurityNumbers = 999_99_9999L
double monetaryAmount = 12_345_132.12
long hexBytes = 0xFF_EC_DE_5E
long hexWords = 0xFFEC_DE5E
long maxLong = 0x7fff_ffff_ffff_ffffL
long alsoMaxLong = 9_223_372_036_854_775_807L
long bytes = 0b11010010_01101001_10010100_10010010
 6
Author: Pankaj Shinde,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-04-13 12:23:49

Reordenar argumentos con argumentos implícitos es otra buena opción.

Este código:

def foo(Map m=[:], String msg, int val, Closure c={}) {
  [...]
}

Crea todos estos métodos diferentes:

foo("msg", 2, x:1, y:2)
foo(x:1, y:2, "blah", 2)
foo("blah", x:1, 2, y:2) { [...] }
foo("blah", 2) { [...] }

Y más. Es imposible meter la pata poniendo argumentos nombrados y ordinales en el orden/posición incorrecta.

Por supuesto, en la definición de "foo", puedes dejar "String" e "int" de "String msg" e "int val" I los dejé solo para mayor claridad.

 5
Author: Robert Fischer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-11-19 22:53:47

Creo que es una combinación de cierres como parámetro y parameter-default-values:

public void buyItems(Collection list, Closure except={it > 0}){
  list.findAll(){except(it)}.each(){print it}
}
buyItems([1,2,3]){it > 2}
buyItems([0,1,2])

Impresiones:"312"

 4
Author: Peter Rader,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-10-25 16:17:27

Usando el operador de propagación en los parámetros del método

Esto es una gran ayuda al convertir código a datos:

def exec(operand1,operand2,Closure op) {
    op.call(operand1,operand2)
}

def addition = {a,b->a+b}
def multiplication = {a,b->a*b}

def instructions = [
     [1,2,addition],
     [2,2,multiplication]
]

instructions.each{instr->
    println exec(*instr)
}

También es útil este uso:

String locale="en_GB"

//this invokes new Locale('en','GB')
def enGB=new Locale(*locale.split('_'))
 4
Author: loteq,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-03-08 13:57:26

Memoization

La memoización es una técnica de optimización que consiste en almacenar los resultados de llamadas a funciones costosas y devolver el resultado almacenado en caché cada vez que se llama de nuevo a la función con los mismos argumentos.

Hay una versión ilimitada, que almacenará en caché cada par de (argumentos de entrada, valor de retorno) que verá; y una versión limitada, que almacenará en caché los últimos N argumentos de entrada vistos y sus resultados, utilizando una caché LRU.

Memoización de métodos:

import groovy.transform.Memoized

@Memoized
Number factorial(Number n) {
    n == 0 ? 1 : factorial(n - 1)
}

@Memoized(maxCacheSize=1000)
Map fooDetails(Foo foo) {
    // call expensive service here
}

Memoización de cierres:

def factorial = {Number n ->
    n == 0 ? 1 : factorial(n - 1)
}.memoize()

fooDetails = {Foo foo ->
    // call expensive service here
}.memoizeAtMost(1000)

La página de Wikipedia tiene amplia información sobre los usos de la Memoización en Ciencias de la Computación. Me limitaré a señalar un simple uso práctico.

Aplazar la inicialización de una constante hasta el último momento posible

A veces tiene un valor constante que no se puede inicializar en la definición de la clase o en el tiempo de creación. Por ejemplo, la expresión constante puede hacer uso de otra constante o un método de una clase diferente, que será conectado por otra cosa (Resorte o tal) después de la inicialización de su clase.

En este caso, puedes convertir tu constante en un getter y decorarla con @Memoized. Solo se calculará una vez, la primera vez que se accede, y luego el valor almacenado en caché y reutilizado:

import groovy.transform.Memoized

@Memoized
def getMY_CONSTANT() {
    // compute the constant value using any external services needed
}
 3
Author: Tobia,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-05-06 10:32:09

Groovy puede funcionar mucho como Javascript. Puede tener vars y funciones privadas a través del cierre. También puede curry funciones con cierres.

class FunctionTests {

def privateAccessWithClosure = {

    def privVar = 'foo'

    def privateFunc = { x -> println "${privVar} ${x}"}

    return {x -> privateFunc(x) } 
}


def addTogether = { x, y ->
    return x + y
}

def curryAdd = { x ->
    return { y-> addTogether(x,y)}
}

public static void main(String[] args) {
    def test = new FunctionTests()

    test.privateAccessWithClosure()('bar')

    def curried = test.curryAdd(5)

    println curried(5)
}
}

Salida:

Foo bar 10

 2
Author: Jason,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-11-20 09:34:54

Invocación del método dinámico

Puede invocar un método usando una cadena con su nombre

class Dynamic {
    def one() { println "method one()" }
    def two() { println "method two()" }
}

def callMethod( obj, methodName ) {
    obj."$methodName"()
}

def dyn = new Dynamic()

callMethod( dyn, "one" )               //prints 'method one()'
callMethod( dyn, "two" )               //prints 'method two()'
dyn."one"()                            //prints 'method one()'
 2
Author: lifeisfoo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-03-23 09:53:22

¿Cómo construir un árbol JSON en un par de líneas en groovy ?

1) define tu árbol con cierre autorreferencial withDefault

def tree // declare  first before using a self reference
tree = { ->  [:].withDefault{ tree() } }

2) Crea tu propio árbol JSON

frameworks = tree()
frameworks.grails.language.name = 'groovy'
frameworks.node.language.name = 'js'

def result =  new groovy.json.JsonBuilder(frameworks)

Que da: {"grails":{"language":{"name":"groovy"}},"node":{"language":{"name":"js"}}}

 2
Author: ludo_rj,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-03-23 20:04:04

Operador de navegación segura

El operador de Navegación segura se utiliza para evitar una excepción NullPointerException. Normalmente, cuando tiene una referencia a un objeto, es posible que deba verificar que no es null antes de acceder a los métodos o propiedades del objeto. Para evitar esto, el operador de navegación segura simplemente devolverá null en lugar de lanzar una excepción, como así:

def person = Person.find { it.id == 123 }        // find will return a null instance    
def name = person?.name                          // use of the null-safe operator prevents from a NullPointerException, result is null
 2
Author: Pankaj Shinde,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-04-13 12:28:48

Múltiples variables desaceleración

1) Declaraciones de variables múltiples en una sola línea

def (a,b,c) = [1,2,3]

2) Utilizando diferentes declaraciones de tipo.

def (String a, int b) = ['Groovy', 1]
 1
Author: Zulqarnain Satti,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-02-11 07:34:46

Operador de coerción

El operador de coerción (as) es una variante del casting. La coerción convierte objetos de un tipo a otro sin que sean compatibles para la asignación. Tomemos un ejemplo:

Entero x = 123
String s = (String) x
Integer no es asignable a una cadena, por lo que producirá una ClassCastException en tiempo de ejecución Esto se puede arreglar usando coerción en su lugar:

Entero x = 123 String s = x como String
Entero no es asignable a una cadena, pero el uso de as lo coaccionará a una cadena

 0
Author: Ishwor,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-12-15 15:23:05