groovy - docs - grails documentation 2.4 4
¿Las características ocultas de Groovy? (30)
Deceleración de variables múltiples
1) Declaraciones de variables múltiples en una sola línea
def (a,b,c) = [1,2,3]
2) Usar diferentes tipos de declaraciones.
def (String a, int b) = [''Groovy'', 1]
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 característica, no solo un enlace a la documentación
- Etiquete la función usando el título en negrita como primera línea
Ver también:
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
Cadena s = (Cadena) x
El entero no es asignable a una Cadena, por lo que producirá una ClassCastException en tiempo de ejecución. Esto se puede solucionar mediante el uso de la coerción:
Entero x = 123 cadena s = x como cadena
El entero no es asignable a una Cadena, pero el uso de lo forzará a una Cadena.
¿Cómo construir un árbol JSON en un par de líneas en groovy?
1) defina su árbol con autorreferencial withDefault
cierre 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"}}}
A diferencia de Java, en Groovy, cualquier cosa se puede usar en una sentencia switch , no solo en tipos primitivos. En un evento típico Método informado
switch(event.source) {
case object1:
// do something
break
case object2:
// do something
break
}
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)
Averiguar qué métodos hay en un objeto es tan fácil como preguntarle a metaClass:
"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"]
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"
El método with permite activar 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
}
Eliminar valores null
de la lista
def list = [obj1, obj2, null, obj4, null, obj6]
list -= null
assert list == [obj1, obj2, obj4, obj6]
En Groovy 1.6, las expresiones regulares funcionan con todos los iteradores de cierre (como cada uno, recopilar, inyectar, etc.) y te 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"]
Groovy puede funcionar mucho como Javascript. Puede tener vars privados y funciones 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
La reordenación de argumentos con argumentos implícitos es otra muy buena.
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 al poner los argumentos nominales y ordinales en el orden / posición incorrectos.
Por supuesto, en la definición de "foo", puede dejar "String" e "int" desde "String msg" e "int val" - Los dejé para mayor claridad.
Las características proporcionadas por las transformaciones dentro del paquete groovy.transform
de GDK, tales como:
-
@Immutable
: la anotación @Imutable instruye al compilador a ejecutar una transformación AST que agrega los captadores, constructores, iguales, hashCode y otros métodos auxiliares necesarios que se escriben normalmente al crear clases inmutables con las propiedades definidas. -
@CompileStatic
: Esto permitirá que el compilador de Groovy use comprobaciones de tiempo de compilación en el estilo de Java y luego realice una compilación estática, omitiendo así el protocolo de objeto meta de Groovy. -
@Canonical
: la anotación @Canonical instruye al compilador para que ejecute una transformación AST que añada constructores posicionales, equals, hashCode y una bonita letra toString a su clase.
Otros:
-
@Slf4j
Esta transformación local agrega una capacidad de registro a su programa mediante el registro LogBack. Cada llamada de método en una variable independiente llamada registro se asignará a una llamada al registrador. - Groovy''s XML Slurper : análisis sencillo de XML. Característica asesina!
Los cierres pueden hacer desaparecer todos los antiguos juegos de 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
}
Para interceptar los métodos estáticos faltantes, use la 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
Para probar el código de Java con Groovy, el generador de gráficos de objetos es sorprendente:
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 aún muy agradable.
(No necesita dar ninguna propiedad de su POJO que sea un valor predeterminado de la lista de una lista vacía en lugar de null
para que funcione el soporte del constructor).
Puede convertir una lista en un mapa utilizando toSpreadMap (), conveniente cuando la orden en la lista sea suficiente para determinar las claves y los valores asociados a 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'']
Sé que estoy un poco tarde, pero creo que faltan algunas buenas características aquí:
Operadores de recolecció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]
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
Usar hashes como pseudo-objetos.
def x = [foo:1, bar:{-> println "Hello, world!"}]
x.foo
x.bar()
Combinado con el tipado de patos, puede recorrer un largo camino con este enfoque. Ni siquiera es necesario sacar al operador "como".
class Foo {
def footest() { return "footest"}
}
class Bar {
@Delegate Foo foo = new Foo()
}
def bar = new Bar()
assert "footest" == bar.footest()
Destructuring
Podría ser llamado algo más en Groovy; se llama desestructuración en clojure. Nunca creerás cuán útil puede venir.
def list = [1, ''bla'', false]
def (num, str, bool) = list
assert num == 1
assert str == ''bla''
assert !bool
Implementación de Interfaz Basada en Cierre
Si tiene una referencia tipeada 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
Invocación de 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()''
Memoization
La memorización es una técnica de optimización que consiste en almacenar los resultados de llamadas a funciones costosas 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 alguna vez verá; y una versión limitada, que almacenará en caché los últimos N argumentos de entrada vistos y sus resultados, utilizando un caché LRU.
Memoració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
}
Memoració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. Solo señalaré un uso práctico simple.
Aplazar la inicialización de una constante al último momento posible
Algunas veces tiene un valor constante que no se puede inicializar en la definición de la clase o 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á conectada por otra cosa (Spring o similar) después de la inicialización de su clase.
En este caso, puede convertir su constante en un getter y decorarlo con @Memoized
. Solo se computará una vez, la primera vez que se acceda, 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
}
Operador de navegación segura
El operador Safe Navigation se usa para evitar una NullPointerException. Normalmente, cuando tiene una referencia a un objeto, es posible que necesite verificar que no sea nulo antes de acceder a los métodos o las 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
Subrayar en literales
Cuando se escriben números literales largos, es más difícil entender cómo se agrupan algunos números, por ejemplo, con grupos de miles, de palabras, etc. Al permitir 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
Usando el operador de nave espacial
Me gusta el operador de nave espacial , útil para todo tipo de escenarios de clasificación personalizados. Algunos ejemplos de uso están here . Una situación en la que es particularmente útil es crear un comparador sobre la marcha de un objeto usando múltiples 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 ]
Usando el operador de puntos extendidos
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() }
.
Usar el operador spread en los parámetros del método
Esto es de 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(''_''))
println
"""
Groovy has "multi-line" strings.
Hooray!
"""