Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Groovy 2.5 and 3.0 (Spanish)

85 views

Published on

En esta presentacion recorro muchas de las nuevas funcionalidades de la version 2.5 y la proxima 3.0 del lenguage de programacion Groovy.

Published in: Technology
  • Be the first to comment

  • Be the first to like this

Groovy 2.5 and 3.0 (Spanish)

  1. 1. GROOVY 2.5+, 3.0 1
  2. 2. HEY HOLA Name Mario Garcia Sigo trabajando en Kaleidos https://kaleidos.net 2 . 1
  3. 3. ME Sospechoso de pertenecer al: Madrid Groovy User Group Social: https://twitter.com/marioggar https://github.com/mariogarcia 2 . 2
  4. 4. 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
  5. 5. GROOVY EN NUMEROS 4 . 1
  6. 6. REFERENCIAS OCI Webminar 23, may 2018 by Paul King Groovy distribution archive 4 . 2
  7. 7. CONTRIBUTORS 30+ nuevos contributors en los ultimos 16 meses 4 . 3
  8. 8. 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
  9. 9. 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
  10. 10. ROADMAP 5 . 1
  11. 11. 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
  12. 12. GROOVY 2.5.X (II) Macros JDK 7 es el minimo Compatible con JDK9/10 con warnings 5 . 3
  13. 13. 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
  14. 14. HABLEMOS DE GROOVY 2.5 6
  15. 15. ANNOTATIONS 7 . 1
  16. 16. 7 . 2
  17. 17. EN GENERAL Flexibilidad en antiguas anotaciones Conversion en Meta anotaciones Validacion de parametros Ayuda en la cobertura de codigo 7 . 3
  18. 18. EJEMPLOS @Immutable @AutoImplement @AutoFinal @NamedXXX @Generated 7 . 4
  19. 19. @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
  20. 20. @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
  21. 21. @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
  22. 22. @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
  23. 23. @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
  24. 24. @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
  25. 25. @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
  26. 26. @IMMUTABLE (OPTIONAL) Considerado immutable tambien package madridgug.immutable import groovy.transform.Immutable @Immutable class Person { String name Integer age Optional<String> mobile } 7 . 12
  27. 27. @AUTOIMPLEMENT A veces tenemos que implementar un interface Pero no queremos / podemos implementarlo entero 7 . 13
  28. 28. @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
  29. 29. @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
  30. 30. @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
  31. 31. @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
  32. 32. @AUTOFINAL A veces cometemos errores re-asignando una variable Deberia avisarnos en tiempo de compilacion 7 . 18
  33. 33. @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
  34. 34. @DELEGATE ENHANCEMENTS Hasta ahora solo se podia utilizar sobre propiedades de la clase 7 . 20
  35. 35. @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
  36. 36. @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
  37. 37. @NAMEDVARIANT Posibilidad de llamar a un metodo con la instancia del tipo requerido o … Descomponiendo la instancia en sus diferentes attributos 7 . 23
  38. 38. @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
  39. 39. @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
  40. 40. @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
  41. 41. @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
  42. 42. @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
  43. 43. @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
  44. 44. MACROS 8 . 1
  45. 45. 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
  46. 46. UTILIDADES macro {} (generacion) MacroClass {} (generacion) @Macro (transformacion) ASTMatcher (filtrado) 8 . 3
  47. 47. MACRO METHOD Nos permite crear statements y expressions De manera mas facil y natural 8 . 4
  48. 48. 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
  49. 49. MACRO METHOD Como crear la guarda antes del codigo ? API AST "A pelo" GeneralUtils para reducirlo macro {} 8 . 6
  50. 50. 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
  51. 51. 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
  52. 52. 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
  53. 53. MACROCLASS Para crear clases y/o metodos con la misma tecnica que con el macro {} method 8 . 10
  54. 54. 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
  55. 55. MACROCLASS Creacion de clase package madridgug.macros.method @Statistics class ProcessorWithStatistics { String name void processX(){} void processY(){} void processZ(){} } 8 . 12
  56. 56. 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
  57. 57. ASTMATCHER Clase de utilidad para filtrar, seleccionar nodos que transformar Una vez que localizas lo que quieres modificar lo modificas 8 . 14
  58. 58. 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
  59. 59. @MACRO Transforman aquellas invocaciones a metodos que tienen el nombre anotado Parecido a las extensiones No imports required 8 . 16
  60. 60. @MACRO public class ExampleMacroMethods { @Macro public static Expression safe(MacroContext macroContext, MethodCa return ternaryX( notNullX(callExpression.getObjectExpression()), callExpression, constX(null) ); } ... } 8 . 17
  61. 61. @MACRO def nullObject = null assert null == safe(safe(nullObject.hashcode()).toString()) 8 . 18
  62. 62. @MACRO nullObject?.hashcode()?.toString() 8 . 19
  63. 63. HABLEMOS DE GROOVY 3.X 9 . 1
  64. 64. ALINEACION CON JDK9 / 10 9 . 2
  65. 65. PARROT Renovacion del parser de Groovy Groovy vuelve a ser un super conjunto de Java Entre otras cosas… lambdas syntax 9 . 3
  66. 66. 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
  67. 67. LAMBDAS Lambdas static Integer applyLambdas2() { return Stream .of(1, 2, 3) .reduce((Integer acc, Integer val) -> { return acc + val }).orElse(1) } 9 . 5
  68. 68. METHOD REFERENCES @CompileStatic static Integer applyMethodReferences(Integer x) { return Optional .ofNullable(x) .map(Functions::x2) .map(inc) .orElse(1) } 9 . 6
  69. 69. 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
  70. 70. 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
  71. 71. LAMBDAS OTHERS Son las lambdas realmente lambdas ? Con @CompileStatic ⇒ lambdas Sin @CompileStatic ⇒ closures 9 . 9
  72. 72. 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
  73. 73. 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
  74. 74. NUEVOS OPERADORES Identidad Elvis Safe indexing 9 . 12
  75. 75. 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
  76. 76. ELVIS void newElvis(Integer x) { x ?= 0 // instead of x ? x : 0 println x + 1 } 9 . 14
  77. 77. SAFE INDEXING void safeIndexing() { def numbers = null println numbers?[10] // will print null } 9 . 15
  78. 78. OTROS Default methods en interfaces !in y !instanceof 9 . 16
  79. 79. @GROOVYDOC Comentarios accesibles en tiempo de ejecucion 9 . 17
  80. 80. @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
  81. 81. @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
  82. 82. QUE QUEDA POR CONTAR ? 10 . 1
  83. 83. GROOVY 2.5 @TupleConstructor Repeated annotations Anotaciones en mas sitios (JSR-308) Mejoras en el CliBuilder with vs tap 10 . 2
  84. 84. GROOVY 3.X Mejora en la syntaxis try with resources 10 . 3
  85. 85. GROOVY ECOSYSTEM 11 . 1
  86. 86. 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
  87. 87. WANNA KNOW MORE ? https://www.youtube.com/watch?v=ECZVbiFZPwE 12
  88. 88. QUESTIONS & ANSWERS 13

×