GROOVY 2.5+, 3.0
1
HEY HOLA
Name
Mario Garcia
Sigo trabajando en Kaleidos
https://kaleidos.net
2 . 1
ME
Sospechoso de pertenecer al:
Madrid Groovy User Group
Social:
https://twitter.com/marioggar
https://github.com/mariogarcia
2 . 2
QUE VENIA A CONTAROS ?
Groovy en numeros
Groovy roadmap 2018 / 2019
Que nos ofrece la version 2.5+
Que nos ofrece la version 3.0
3
GROOVY EN NUMEROS
4 . 1
REFERENCIAS
OCI Webminar 23, may 2018 by Paul King
Groovy distribution archive
4 . 2
CONTRIBUTORS
30+ nuevos contributors en los ultimos 16 meses
4 . 3
DESCARGAS
23 millones 2016
50 millones 2017
19 millones solo en el 1er Q de 2018
+ 4 millones de descargas al mes y subiendo
4 . 4
ULTIMAS VERSIONES
2.4.15: 2018-09-04
2.5.3: 2018-10-14
2.6.0-alpha-4: 2018-06-26
3.0.0-alpha-3: 2018-06-26
4 . 5
ROADMAP
5 . 1
GROOVY 2.5.X
La 2.5.x ya lleva con nosotros 5 meses (2018-05-30)
Actualizaciones a buen ritmo
2.5.0: mayo
2.5.1: julio
2.5.2: agosto
5 . 2
GROOVY 2.5.X (II)
Macros
JDK 7 es el minimo
Compatible con JDK9/10 con warnings
5 . 3
GROOVY 2.6, 3.0
Alphas de ambas desde mediados de 2018
Parrot parser (JDK8 syntax 1 - 1 y mucho mas)
2.6
JDK7 (min)
Corta esperanza de vida
5 . 4
HABLEMOS DE GROOVY 2.5
6
ANNOTATIONS
7 . 1
7 . 2
EN GENERAL
Flexibilidad en antiguas anotaciones
Conversion en Meta anotaciones
Validacion de parametros
Ayuda en la cobertura de codigo
7 . 3
EJEMPLOS
@Immutable
@AutoImplement
@AutoFinal
@NamedXXX
@Generated
7 . 4
@IMMUTABLE
No poder modificar los campos de una clase
Si los campos son otras clases…
Comprueba que sean immutables tambien
O que esten en una lista blanca de clases
7 . 5
@IMMUTABLE (OLD)
package madridgug.immutable
import groovy.transform.Immutable
@Immutable
class Car {
Brand brand (1)
String model
Double hp
Map<?, String> extra (2)
}
7 . 6
@IMMUTABLE (NEW - JAVA.TIME)
package madridgug.immutable
import java.time.LocalTime
import groovy.transform.Immutable
@Immutable
class Brand {
String name
LocalTime birthdate (1)
}
7 . 7
@IMMUTABLE (NEW - DI FRIENDLY)
package madridgug.immutable
import groovy.transform.Canonical
import groovy.transform.ImmutableBase
import groovy.transform.PropertyOptions
import groovy.transform.options.ImmutablePropertyHandler
@ImmutableBase
@Canonical(defaults = false)
@PropertyOptions(propertyHandler = ImmutablePropertyHandler)
class SecurityConfig {
URI provider
String key
}
7 . 8
@IMMUTABLE (DI FRIENDLY - II)
+ 1 vez ⇒ Meta anotacion
package madridgug.immutable
import groovy.transform.*
import groovy.transform.options.*
@ImmutableBase
@TupleConstructor(defaults = false)
@PropertyOptions(propertyHandler = ImmutablePropertyHandler)
@AnnotationCollector
@interface ImmutableDI {
}
7 . 9
@IMMUTABLE (DI FRIENDLY - III)
Annotated class
Google Guice Module
package madridgug.immutable
@ImmutableDI
class SecurityConfig2 {
URI provider
String key
}
def ctor = SecurityConfig2.getConstructor(URI, String)
bind(SecurityConfig2)
.toConstructor(ctor)
.in(Scopes.SINGLETON)
7 . 10
@IMMUTABLE (DI FRIENDLY - IV)
given: 'a Google Guice Injector'
Injector di = Guice.createInjector(new GuiceModule())
when: 'getting the configuration instance'
SecurityConfig2 config = di.getInstance(SecurityConfig2)
then: 'properties should've been populated properly'
config.key == 'key'
config.provider == URI.create('https://nowhereprovider.io')
when: 'trying to change properties again'
config.key = 'another key'
then: 'an error will be thrown'
thrown(ReadOnlyPropertyException)
7 . 11
@IMMUTABLE (OPTIONAL)
Considerado immutable tambien
package madridgug.immutable
import groovy.transform.Immutable
@Immutable
class Person {
String name
Integer age
Optional<String> mobile
}
7 . 12
@AUTOIMPLEMENT
A veces tenemos que implementar un interface
Pero no queremos / podemos implementarlo entero
7 . 13
@AUTOIMPLEMENT
package madridgug.autoimplement
interface MessageSender {
/**
* Sends a message {@link String}
*/
UUID sendMessage(String message)
/**
* Checks whether the message has been received yet or not
*/
boolean isMessageReceived(UUID messageId)
/**
* Checks whether the message has been sent yet or not
*/
Boolean isMessageSent(UUID messageId)
}
7 . 14
@AUTOIMPLEMENT
Default
package madridgug.autoimplement
import groovy.util.logging.Slf4j
import groovy.transform.AutoImplement
@Slf4j
@AutoImplement
class PartialMessageSender implements MessageSender {
@Override
UUID sendMessage(String message) {
log.info "message: $message"
return UUID.randomUUID()
}
}
7 . 15
@AUTOIMPLEMENT
Throw exceptions
package madridgug.autoimplement
import madridgug.exception.NotEnoughTimePalException
import groovy.util.logging.Slf4j
import groovy.transform.AutoImplement
@Slf4j
@AutoImplement(exception = NotEnoughTimePalException)
class NoisyMessageSender implements MessageSender {
@Override
UUID sendMessage(String message) {
log.info "message: $message"
return UUID.randomUUID()
}
7 . 16
@AUTOIMPLEMENT
Custom code
package madridgug.autoimplement
import static java.time.LocalDateTime.now
import groovy.util.logging.Slf4j
import groovy.transform.AutoImplement
@Slf4j
@AutoImplement(code = {
log.error("Necesito + tiempo ok? Prueba a las: ${now() + 120}")
})
class VeryNoisyMessageSender implements MessageSender {
}
7 . 17
@AUTOFINAL
A veces cometemos errores re-asignando una variable
Deberia avisarnos en tiempo de compilacion
7 . 18
@AUTOFINAL
package madridgug.autofinal
import groovy.transform.AutoFinal
class WordProcessor {
@AutoFinal
String reverseWord(Word word) {
// word = new Word(word: 'another') (1)
return word.reverse()
}
Integer size(Word word) {
word = new Word(word: 'another') (2)
return word.size()
}
7 . 19
@DELEGATE ENHANCEMENTS
Hasta ahora solo se podia utilizar sobre propiedades de la
clase
7 . 20
@DELEGATE ENHANCEMENTS
Ahora @Delegate se puede usar en getters
package madridgug.delegate
class TennisPlayer {
List<Map> wins
String name
@Delegate
Integer getWinSize() {
return wins.size()
}
}
7 . 21
@DELEGATE ENHANCEMENTS
given: 'two players'
def good = new TennisPlayer(name: 'John', wins: [
[name: 'Roland Garros 2014'],
[name: 'Roland Garros 2015'],
[name: 'Roland Garros 2016']
])
def bad = new TennisPlayer(name: 'Peter', wins: [
[name: 'Somewhere 2002']
])
and: 'the good one should be greater than the bad one'
good.intValue() > bad.intValue()
7 . 22
@NAMEDVARIANT
Posibilidad de llamar a un metodo con la instancia del
tipo requerido o …
Descomponiendo la instancia en sus diferentes attributos
7 . 23
@NAMEDVARIANT
package madridgug.namedvariant
class Car {
String brand
String model
Integer hp
}
package madridgug.namedvariant
import groovy.util.logging.Slf4j
import groovy.transform.NamedVariant
import groovy.transform.NamedDelegate
@Slf4j
class CarProcessor {
@NamedVariant
void process(@NamedDelegate Car car) {
log.info "processing: ${car.model}"
}
}
7 . 24
@NAMEDVARIANT
Pasando una instancia
Car car = new Car(brand: 'ferrari', model: 'F450', hp: 450)
CarProcessor processor = new CarProcessor()
when: 'processing the car'
processor.process(car)
7 . 25
@NAMEDVARIANT
Pasando propiedades
and: 'a processor instance'
CarProcessor processor = new CarProcessor()
when: 'processing the car'
processor.process(brand: 'ferrari', model: 'F450', hp: 450)
7 . 26
@NAMEDVARIANT
NamedParam
package madridgug.namedvariant
import java.time.LocalDateTime
import groovy.util.logging.Slf4j
import groovy.transform.*
@Slf4j
class AnotherCarProcessor {
@NamedVariant
void process(String user, @NamedParam LocalDateTime now,
@NamedDelegate Car car) {
log.info "$user ha procesado el modelo ${car.model} el $now"
}
}
7 . 27
@NAMEDVARIANT
Invocacion
and: 'a processor instance'
AnotherCarProcessor processor = new AnotherCarProcessor()
when: 'processing the car'
processor.process(
'John Doe',
now: LocalDateTime.now(),
brand: 'ferrari',
model: 'F450',
hp: 450,
)
7 . 28
@GENERATED
Concebido para los autores de transformciones AST
Ayuda a las herramientas de cobertura de codigo
Para saber que codigo es generado y cual no
Hasta ahora ni JaCoCo ni Cobertura lo tenian facil
7 . 29
MACROS
8 . 1
LAS UTILIDADES DE MACROS
ABSTRAEN…
La generacion de nuevo codigo
El filtrado de codigo sobre el que quieres aplicar una
transformacion
La transformacion de codigo
8 . 2
UTILIDADES
macro {} (generacion)
MacroClass {} (generacion)
@Macro (transformacion)
ASTMatcher (filtrado)
8 . 3
MACRO METHOD
Nos permite crear statements y expressions
De manera mas facil y natural
8 . 4
MACRO METHOD
Este codigo
Deberia contemplar este test
package madridgug.macros.method
class SafeCalls {
@MakeParamSafe
static Integer inc(Integer a) {
// if (!a) { return 1 }
return a + 1
}
}
void 'check unsafe calls'() {
expect: 'unsafe calls to return ok'
SafeCalls.inc(null) == 1
}
8 . 5
MACRO METHOD
Como crear la guarda antes del codigo ?
API AST "A pelo"
GeneralUtils para reducirlo
macro {}
8 . 6
MACRO METHOD
API ASTs
GeneralUtils
IfStatement safeGuard = new IfStatement(
new NotExpression(
new VariableExpression(paramName)
),
new ReturnStatement(new ConstantExpression(1)),
new EmptyStatement()
)
IfStatement safeGuard = ifS(
notX(varX(paramName)),
returnS(constX(1))
) as IfStatement
8 . 7
MACRO METHOD
macro {}
1 Las expressiones se interpolan
2 Dentro de codigo plano
VariableExpression paramRef = Utils.getParamAsVarX(methodNode) (1)
IfStatement safeGuard = macro {
if (! $v { paramRef }) { (2)
return 1
}
}
8 . 8
MACRO METHOD
Mejor caso de uso
BlockStatement getMD5Code(final String propertyName) {
return macro(true) {
java.security.MessageDigest.getInstance('MD5')
.digest(${propertyName}.getBytes('UTF-8'))
.encodeHex()
.toString()
}
}
8 . 9
MACROCLASS
Para crear clases y/o metodos con la misma tecnica que
con el macro {} method
8 . 10
MACROCLASS
Creacion de clase
@CompileDynamic
ClassNode buildTemplateClass(ClassNode reference) {
def methodCount = constX(reference.methods.size())
def fieldCount = constX(reference.fields.size())
return new MacroClass() {
class Statistics {
java.lang.Integer getMethodCount() {
return $v { methodCount }
}
java.lang.Integer getFieldCount() {
return $v { fieldCount }
}
}
}
8 . 11
MACROCLASS
Creacion de clase
package madridgug.macros.method
@Statistics
class ProcessorWithStatistics {
String name
void processX(){}
void processY(){}
void processZ(){}
}
8 . 12
MACROCLASS
Creacion de clase
void 'check statistics'() {
given: 'an instance of processor'
ProcessorWithStatistics processor = new ProcessorWithStatistics()
expect: 'number of methods'
processor.methodCount == 3
and: 'number of fields'
processor.fieldCount == 1
}
8 . 13
ASTMATCHER
Clase de utilidad para filtrar, seleccionar nodos que
transformar
Una vez que localizas lo que quieres modificar lo
modificas
8 . 14
ASTMATCHER
@Override
Expression transform(Expression exp) {
Expression ref = macro { 1 + 1 }
if (ASTMatcher.matches(ref, exp)) {
return macro { 3 }
}
return super.transform(exp)
}
8 . 15
@MACRO
Transforman aquellas invocaciones a metodos que tienen
el nombre anotado
Parecido a las extensiones
No imports required
8 . 16
@MACRO
public class ExampleMacroMethods {
@Macro
public static Expression safe(MacroContext macroContext, MethodCa
return ternaryX(
notNullX(callExpression.getObjectExpression()),
callExpression,
constX(null)
);
}
...
}
8 . 17
@MACRO
def nullObject = null
assert null == safe(safe(nullObject.hashcode()).toString())
8 . 18
@MACRO
nullObject?.hashcode()?.toString()
8 . 19
HABLEMOS DE GROOVY 3.X
9 . 1
ALINEACION CON JDK9 / 10
9 . 2
PARROT
Renovacion del parser de Groovy
Groovy vuelve a ser un super conjunto de Java
Entre otras cosas… lambdas syntax
9 . 3
LAMBDAS
Lambdas
static Integer applyLambdas() {
return Stream
.of(1, 2, 3)
.filter(y -> y % 2 == 0)
.map(y -> y + 1)
.mapToInt(Integer::intValue)
.sum()
}
9 . 4
LAMBDAS
Lambdas
static Integer applyLambdas2() {
return Stream
.of(1, 2, 3)
.reduce((Integer acc, Integer val) -> {
return acc + val
}).orElse(1)
}
9 . 5
METHOD REFERENCES
@CompileStatic
static Integer applyMethodReferences(Integer x) {
return Optional
.ofNullable(x)
.map(Functions::x2)
.map(inc)
.orElse(1)
}
9 . 6
LAMBDAS + CLOSURES + M. REF.
Todo junto
static Integer applyBoth() {
return Stream
.of(1, 2, 3)
.filter { y -> y % 2 == 0 } // closure syntax
.map( y -> y + 1 ) // lambda syntax
.mapToInt(Integer::intValue) // method ref
.sum()
}
9 . 7
LAMBDAS OTHERS
// Sin parentesis
static Function<Integer, Integer> inc = x -> x + 1
// Con valor por defecto (No existe en Java)
static Function<Integer, Integer> dob = (x = 0) -> x * 2
// Con parentesis
static Function<Integer, Integer> tri = (Integer x) -> x * 3
// Con llaves
static Function<Integer, Integer> com = (Integer x) -> {
x + 4
}
9 . 8
LAMBDAS OTHERS
Son las lambdas realmente lambdas ?
Con @CompileStatic ⇒ lambdas
Sin @CompileStatic ⇒ closures
9 . 9
LOOPS
En general todos los tipos de loops que se puedan hacer
en Java
Ahora se puede hacer un do / while por ejemplo
9 . 10
LOOPS
Multi asignacion
List<String> returnValues() {
List<String> values = []
for (def (String u, Integer v) = ['bar', 42]; v < 45; u++, v++) {
values << "$u $v"
}
return values
}
9 . 11
NUEVOS OPERADORES
Identidad
Elvis
Safe indexing
9 . 12
IDENTIDAD
package madridgug.parrot
import groovy.transform.Immutable
class Identity {
@Immutable
class Person {
String name
}
void checkIdentity(Person left, Person right) {
if (left === right) { (1)
log.info "es la misma persona"
}
if (left == right) { (2)
log.info "son personas con caracteristicas iguales"
}
9 . 13
ELVIS
void newElvis(Integer x) {
x ?= 0 // instead of x ? x : 0
println x + 1
}
9 . 14
SAFE INDEXING
void safeIndexing() {
def numbers = null
println numbers?[10] // will print null
}
9 . 15
OTROS
Default methods en interfaces
!in y !instanceof
9 . 16
@GROOVYDOC
Comentarios accesibles en tiempo de ejecucion
9 . 17
@GROOVYDOC
package madridgug.parrot
class Processor {
/**
* @Groovydoc accesible1 (1)
*/
void process(String word) {
println "process: $word"
}
@Groovydoc('accesible2') (2)
void check(String word) {
println "check: $word"
}
}
9 . 18
@GROOVYDOC
comments
annotation
String content = Processor
.methods
.find { it.name == 'process' }
.groovydoc
.content
assert content == 'accesible1 // <1>'
String content = Processor
.methods
.find { it.name == 'check' }
.groovydoc
.content
assert content == 'accesible2'
9 . 19
QUE QUEDA POR CONTAR ?
10 . 1
GROOVY 2.5
@TupleConstructor
Repeated annotations
Anotaciones en mas sitios (JSR-308)
Mejoras en el CliBuilder
with vs tap
10 . 2
GROOVY 3.X
Mejora en la syntaxis try with resources
10 . 3
GROOVY ECOSYSTEM
11 . 1
BIBLIOTECAS Y FRAMEWORKS
Nombre Version Groovy
Spock 1.2-groovy-2.5 2.5.2
Micronaut 1.0.0-RC2 2.5.1
Geb 2.2 2.4.15
Grails 3.3.8 2.4.15
Griffon 2.15.0 2.4.15
11 . 2
WANNA KNOW MORE ?
https://www.youtube.com/watch?v=ECZVbiFZPwE
12
QUESTIONS & ANSWERS
13

Groovy 2.5 and 3.0 (Spanish)