¿Características ocultas de Groovy?

78

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

  • Intenta limitar las respuestas al núcleo de Groovy
  • Una característica por respuesta
  • Dé un ejemplo y una breve descripción de la función, no solo un enlace a la documentación.
  • Etiquete la función con un título en negrita como primera línea

Ver también:

  1. Características ocultas de Python
  2. Características ocultas de Ruby
  3. Características ocultas de Perl
  4. Funciones ocultas de Java
anon
fuente

Respuestas:

56

Usando el operador de punto de dispersión

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() }.

Paul King
fuente
¿Qué significa la tercera línea?
ripper234
7
Desde el contexto, significa invocar el método de tamaño en cada elemento de la matriz y devolver una matriz de los resultados. Bastante genial en realidad :-)
Michael Rutherfurd
39

El método with permite convertir esto:

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

dentro de esto

 myObj1.with {
    value = 10
    otherObj.title = name
    mode = MODE_NORMAL
 }
Rui Vieira
fuente
3
eso me trae viejos recuerdos sobre el objeto pascal :-)
fortran
1
Dado que esto es Groovy, ¿no sería una comparación más típica entre myObj1.value = 10(etc.) y este último, ya que no necesita invocar un método de establecimiento?
Philip
37

Usando hashes como pseudoobjetos.

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

Combinado con la escritura de pato, puede recorrer un largo camino con este enfoque. Ni siquiera necesita sacar el operador "como".

Robert Fischer
fuente
2
nuevo en Groovy, eso es muy agradable.
Steve B.
37

Alguien sabe sobre 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)
Bill James
fuente
1
¿Es esto lo mismo que el operador de fusión nula (??) de C #?
Alex Baranosky
Parece que sí, aunque tuve que buscar el C # op.
billjamesdev
No exactamente, es un operador ternario abreviado. Hice un artículo sobre él: colinharrington.net/blog/2008/10/groovy-elvis-operator También puede hacer expresiones completas allí :-)
Colin Harrington
El código publicado en la respuesta no se compila porque una palabra clave, "predeterminado", se usa como variable. Utilice "d" en su lugar para compilar el código.
Vorg van Geir
2
No hay ninguna razón importante en absoluto, solo de acuerdo con la convención que sugirió el OP. En ese momento no tomé en consideración el efecto refrescante que causaría mi acción.
gotomanners
35

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

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

huellas dactilares:

["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"]
Ted Naleid
fuente
1
Esto parece una tontería al principio. Pero es increíblemente útil. En Python tienes la función incorporada dir: dir ("foo") da todos los métodos para una cadena.
santiagobasulto
28

Para interceptar los métodos estáticos que faltan, utilice 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

ken
fuente
Nuevo en Groovy, tengo un poco de dificultad para analizar esto.
ripper234
3
El Object Foo no tiene un método estático llamado B definido. Sin embargo, puede implementar uno sobre la marcha agregando un método llamado "$ static_methodMissing (String, Object)" e implementando lo que desee allí. Este método mágico se llama siempre que se invoca un método estático y el objeto no tiene definido ese método estático.
Jen S.
24

Desestructuración

Podría llamarse de otra manera en Groovy; se llama desestructuración en clojure. Nunca creerá lo útil que puede resultar.

def list = [1, 'bla', false]
def (num, str, bool) = list
assert num == 1
assert str == 'bla'
assert !bool
Bojan Dolinar
fuente
Se llama asignación múltiple en Groovy. Wikipedia lo llama asignación paralela .
Frank Kusters
La desestructuración en Clojure es mucho más poderosa que la asignación múltiple. clojure.org/…
Jason
21

Para probar el código java con groovy, el generador de gráficos de objetos 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 muy agradable.

ObjectGraphBuilder

(Debe proporcionar cualquier propiedad de su POJO que sea Listun valor predeterminado de una lista vacía en lugar de nullque funcione el soporte del constructor).

krosenvold
fuente
19
println 
"""
Groovy has "multi-line" strings.
Hooray!
"""
Brian
fuente
Ah, la belleza de las cuerdas de varias líneas. Todos los idiomas deberían adoptarlos.
ripper234
2
No estoy seguro de por qué una cadena de varias líneas necesita "" "como delimitador cuando" podría haberse extendido para permitir cadenas de varias líneas y de una sola línea.
Vorg van Geir
2
@VorgvanGeir usando "" "significa que no tienes que escapar".
undefined
1
@Brian True, pero "" "a \ bc" de "f \ g" "" no se compila porque tienes que escapar de \ o \ g, y \ b actuará como un retroceso a menos que lo escapes. ¿Cuál es el sentido de no necesitar escapar "cuando todavía necesitas escapar de CADA otra secuencia especial dentro de una cadena?
Vorg van Geir
Porque algunos de nosotros queremos poder escribir "foo \ tbar". Pero Groovy también tiene: println (/ a \ bc "de" f \ g /) // -> a \ bc "de" f \ g
DarkStar
15

En groovy 1.6, las expresiones regulares funcionan con todos los iteradores de cierre (como cada uno, recopilar, inyectar, 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"]
Ted Naleid
fuente
15

A diferencia de Java, en Groovy, cualquier cosa se puede usar en una declaración de cambio , no solo tipos primitivos. En un evento típico Método realizado

switch(event.source) {
   case object1:
        // do something
        break
   case object2:
        // do something
        break
}
Rui Vieira
fuente
15

Usando el operador de nave espacial

Me gusta el operador Spaceship , útil para todo tipo de escenarios de clasificación personalizados. Algunos ejemplos de uso están aquí . Una situación en la que es particularmente útil es crear un comparador sobre la marcha de un objeto utilizando varios campos. p.ej

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 ]
mmigdol
fuente
14

Los cierres pueden hacer que desaparezcan todos los viejos juegos de la gestión de recursos. La secuencia de archivos se cierra automáticamente al final del bloque:

new File("/etc/profile").withReader { r ->
    System.out << r
}
John Flinchbaugh
fuente
1
Y también el identificador de archivo se cierra correctamente en caso de que se genere una excepción dentro del cierre, me gusta esto incluso más que try-with-resources.
DarkStar
13

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

  • @Immutable: La anotación @Immutable indica al compilador que ejecute una transformación AST que agrega los getters, constructores, equals, hashCode y otros métodos auxiliares necesarios que normalmente se escriben al crear clases inmutables con las propiedades definidas.
  • @CompileStatic: Esto permitirá que el compilador Groovy utilice comprobaciones en tiempo de compilación al estilo de Java y luego realice una compilación estática, evitando así el protocolo de metaobjetos Groovy.
  • @Canonical: La anotación @Canonical indica al compilador que ejecute una transformación AST que agrega constructores posicionales, equals, hashCode y una bonita impresión toString a su clase.

Otros:

  • @Slf4jEsta transformación local agrega una capacidad de registro a su programa mediante el registro LogBack. Cada llamada a un método en una variable independiente denominada log se asignará a una llamada al registrador.
  • Slurper XML de Groovy : análisis sencillo de XML. ¡Característica asesina!
Hans Westerbeek
fuente
12

Puede convertir una lista en un mapa usando 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']
conocido
fuente
¿Es necesario esto as Object []en primera línea?
Kamil
12

Implementación de interfaz basada en cierres

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
Don
fuente
8

Eliminar nullvalores de la lista

def list = [obj1, obj2, null, obj4, null, obj6]
list -= null
assert list == [obj1, obj2, obj4, obj6]
BIdesi
fuente
7

Sé que llegué un poco tarde, pero creo que faltan algunas características interesantes aquí:

Colección más / menos operadores

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]

Declaración de cambio

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
micha
fuente
7

@Delegar

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

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

def bar = new Bar()

assert "footest" == bar.footest()
Omnipresente
fuente
6

Subrayar en literales

Al escribir números literales largos, es más difícil de entender cómo se agrupan algunos números, por ejemplo, con grupos de miles, de palabras, etc. Al permitirle colocar un guión bajo 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
Pankaj Shinde
fuente
5

El reordenamiento de argumentos con argumentos implícitos es otro buen método.

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 equivocarse poniendo argumentos ordinales y con nombre en el orden / posición incorrectos.

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

Robert Fischer
fuente
Desearía que este fuera el caso, pero actualmente Groovy (1.6) solo admite argumentos con nombre para constructores de objetos. Puede usar esta sintaxis para llamadas a métodos, pero empaqueta los argumentos con nombre en un mapa y luego busca foo (mapa).
Cody Casterline
Estoy confundido en cuanto a lo que crees que dije que implicaba algo diferente.
Robert Fischer
4

Creo que es una combinación de cierres como parámetro y valores predeterminados de parámetro:

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"

Severo
fuente
4

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

Esta es una gran ayuda al convertir código en 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('_'))
Luis Muñiz
fuente
No, me refiero a convertir código en datos, hacer una matriz que sea de datos, equivalente a una lista de argumentos, que normalmente es estática. Pero entiendo tu punto, depende de tu perspectiva. Lo estoy viendo desde el punto de vista de la refactorización del código estático existente a más código dynami! C.
Luis Muñiz
¿Quizás "cuando se utiliza un diseño basado en datos"?
DarkStar
3

Memorización

La memorización es una técnica de optimización que consiste en almacenar los resultados de costosas llamadas a funciones y devolver el resultado en caché cada vez que se vuelve a llamar 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.

Memorizació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
}

Memorizació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 memorización en informática. Solo señalaré un uso práctico simple.

Diferir 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 momento de la 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 (Spring o similar) después de la inicialización de su clase.

En este caso, puede convertir su constante en un captador y decorarlo con @Memoized. Solo se calculará una vez, la primera vez que se acceda, y luego el valor se almacenará en caché y se reutilizará:

import groovy.transform.Memoized

@Memoized
def getMY_CONSTANT() {
    // compute the constant value using any external services needed
}
Tobia
fuente
2

Groovy puede funcionar de manera muy similar a Javascript. Puede tener variables y funciones privadas mediante el 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

Jason
fuente
2

Invocación de método dinámico

Puedes 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()'
lifeisfoo
fuente
2

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

1) define tu árbol con withDefaultcierre autorreferencial

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)

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

ludo_rj
fuente
2

Operador de navegación seguro

El operador Safe Navigation se utiliza para evitar una NullPointerException. Normalmente, cuando tiene una referencia a un objeto, es posible que deba verificar que no sea nulo antes de acceder a los métodos o propiedades del objeto. Para evitar esto, el operador de navegación segura simplemente devolverá nulo en lugar de lanzar una excepción, 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
Pankaj Shinde
fuente
1

Desaceleración de múltiples variables

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

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

2) Uso de declaraciones de tipos diferentes.

def (String a, int b) = ['Groovy', 1]
Zulqarnain Satti
fuente
0

Operador de coacción

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

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

Integer x = 123 String s = x como String
Integer no se puede asignar a una String, pero el uso de as lo forzará a una String

Ishwor
fuente