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.

Madrid gug - sacando partido a las transformaciones ast de groovy

1,030 views

Published on

Groovy es un gran lenguaje con capacidades muy potentes de metaprogramación en tiempo de compilación. ¿Sabías que hay más de 40 transformaciones AST disponibles para hacernos la vida más fácil como desarrolladores?

En esta charla aprenderás las transformaciones más importantes que proporciona Groovy a través de muchos ejemplos para explicar todos los conceptos.

Published in: Technology
  • Be the first to comment

  • Be the first to like this

Madrid gug - sacando partido a las transformaciones ast de groovy

  1. 1. Sacando partido a las transformaciones AST de Groovy IVÁN LÓPEZ @ilopmar
  2. 2. ➢ Iván López - @ilopmar ➢ Groovy & Grails developer Member of Grails team at OCI ➢ @MadridGUG coordinator http://www.madridgug.com ➢ Greach organizer (@greachconf) http://greachconf.com ➢ Speaker: SpringOne 2GX, GR8Conf, Codemotion, GeeCon, Spring IO, Greach, JavaCro, RigaDevDay,... About me...
  3. 3. “ The best code is not code at all
  4. 4. 1. A little bit of theory
  5. 5. AST and compilation ▷ Abstract Syntax Tree ▷ AST modified during compilation ▷ Hook into the compiler phases
  6. 6. AST Transformations ▷ Global ▷ Local
  7. 7. 2. Out-of-the-box ASTs
  8. 8. AST transformations categories ▷ Code generation ▷ Class design ▷ Logging improvements ▷ Declarative concurrency ▷ Cloning and externalizing ▷ Safe scripting ▷ Compiler directives ▷ Dependencies handling
  9. 9. Code generation
  10. 10. @ToString ▷ Human readable toString ▷ Effective Java by Joshua Bloch (item 10)
  11. 11. class User { String name Integer age } def u = new User(name: 'Iván', age: 37) println u // User@1d2a54b2
  12. 12. @groovy.transform.ToString class User { String name Integer age } def u = new User(name: 'Iván', age: 37) assert u.toString() == 'User(Iván, 37)' class User { String name Integer age } def u = new User(name: 'Iván', age: 37) println u // User@1d2a54b2
  13. 13. @groovy.transform.ToString class User { String name Integer age } def u = new User(name: 'Iván', age: 37) assert u.toString() == 'User(Iván, 37)' String toString() { def _result = new StringBuilder() _result.append('User(') _result.append(this.name) _result.append(', ') _result.append(this.age) _result.append(')') return _result.toString() } class User { String name Integer age } def u = new User(name: 'Iván', age: 37) println u // User@1d2a54b2
  14. 14. @ToString ▷ includeNames, excludes, includes, includeSuper, includeSuperProperties, includeFields, ignoreNulls, includePackage, cache
  15. 15. @ToString ▷ includeNames, excludes, includes, includeSuper, includeSuperProperties, includeFields, ignoreNulls, includePackage, cache @groovy.transform.ToString(includeNames = true, excludes = ['name']) class User { String name Integer age } def u = new User(name: 'Iván', age: 37) assert u.toString() == 'User(age:37)'
  16. 16. @EqualsAndHashCode ▷ Generate equals and hashCode implementations ▷ Effective Java items 8 & 9
  17. 17. @EqualsAndHashCode ▷ Generate equals and hashCode implementations ▷ Effective Java items 8 & 9 @groovy.transform.EqualsAndHashCode class User { String name Integer age } def u1 = new User(name: 'Iván', age: 37) def u2 = new User(name: 'Iván', age: 37) assert u1 == u2 assert u1.hashCode() == u2.hashCode()
  18. 18. int hashCode() { def _result = HashCodeHelper.initHash() _result = HashCodeHelper.updateHash(_result, this.name) _result = HashCodeHelper.updateHash(_result, this.age) return _result } boolean canEqual(Object other) { return other instanceof User } boolean equals(Object other) { if (other == null) { return false } if (this.is(other)) { return true } if (!(other instanceof User)) { return false } User otherTyped = ((other) as User) if (!(otherTyped.canEqual(this))) { return false } if (!(this.getName().is(otherTyped.getName()))) { if (this.getName().is(this) && !(otherTyped.getName().is(otherTyped)) || !(this.getName().is(this)) && otherTyped.getName().is(otherTyped)) return false } else { if (!(this.getName().is(this) && otherTyped.getName().is(otherTyped))) { if (!(this.getName() == otherTyped.getName())) { return false } } } } if (!(this.getAge().is(otherTyped.getAge()))) { if (this.getAge().is(this) && !(otherTyped.getAge().is(otherTyped)) || !(this.getAge().is(this)) && otherTyped.getAge().is(otherTyped)) { return false } else { if (!(this.getAge().is(this) && otherTyped.getAge().is(otherTyped))) { if (!(this.getAge() == otherTyped.getAge())) { return false } } } } return true }
  19. 19. @EqualsAndHashCode ▷ excludes, includes, callSuper, includeFields, cache, useCanEqual
  20. 20. @EqualsAndHashCode ▷ excludes, includes, callSuper, includeFields, cache, useCanEqual @groovy.transform.EqualsAndHashCode(includes = 'name') class User { String name Integer age } def u1 = new User(name: 'Iván', age: 37) def u2 = new User(name: 'Iván', age: 42) assert u1 == u2 assert u1.hashCode() == u2.hashCode()
  21. 21. @TupleConstructor ▷ Generate constructors
  22. 22. @TupleConstructor ▷ Generate constructors @groovy.transform.TupleConstructor class User { String name Integer age }
  23. 23. @TupleConstructor ▷ Generate constructors @groovy.transform.TupleConstructor class User { String name Integer age } // Default map constructor def u1 = new User(name: 'Iván', age: 37)
  24. 24. @TupleConstructor ▷ Generate constructors @groovy.transform.TupleConstructor class User { String name Integer age } // Default map constructor def u1 = new User(name: 'Iván', age: 37) // Generated tuple constructor def u2 = new User('Iván', 37) def u3 = new User('Iván')
  25. 25. @TupleConstructor ▷ Generate constructors @groovy.transform.TupleConstructor class User { String name Integer age } // Default map constructor def u1 = new User(name: 'Iván', age: 35) // Generated tuple constructor def u2 = new User('Iván', 37) def u3 = new User('Iván') User(String name = null, Integer age = null) { this.name = name this.age = age }
  26. 26. @TupleConstructor ▷ excludes, includes, includeFields, includeProperties, includeSuperFields, includeSuperProperties, callSuper, force
  27. 27. @Canonical ▷ @ToString + @EqualsAndHashCode + @TupleConstructor
  28. 28. @Canonical ▷ @ToString + @EqualsAndHashCode + @TupleConstructor @groovy.transform.Canonical class User { String name Integer age }
  29. 29. @Canonical ▷ @ToString + @EqualsAndHashCode + @TupleConstructor def u1 = new User(name: 'Iván', age: 37) assert u1.toString() == 'User(Iván, 37)' // @ToString @groovy.transform.Canonical class User { String name Integer age }
  30. 30. @Canonical ▷ @ToString + @EqualsAndHashCode + @TupleConstructor def u2 = new User('Iván', 37) // @TupleConstructor assert u2.toString() == 'User(Iván, 37)' def u1 = new User(name: 'Iván', age: 37) assert u1.toString() == 'User(Iván, 37)' // @ToString @groovy.transform.Canonical class User { String name Integer age }
  31. 31. @Canonical ▷ @ToString + @EqualsAndHashCode + @TupleConstructor assert u1 == u2 // @EqualsAndHashCode assert u1.hashCode() == u2.hashCode() // @EqualsAndHashCode def u2 = new User('Iván', 37) // @TupleConstructor assert u2.toString() == 'User(Iván, 37)' def u1 = new User(name: 'Iván', age: 37) assert u1.toString() == 'User(Iván, 37)' // @ToString @groovy.transform.Canonical class User { String name Integer age }
  32. 32. @InheritConstructors ▷ Reduce boilerplate code when parent classes have multiple constructors ▷ Useful when overriding exception classes
  33. 33. @groovy.transform.InheritConstructors class MyException extends Exception { }
  34. 34. protected MyException(String param0, Throwable param1, boolean param2, boolean param3) { super(param0, param1, param2, param3) } public MyException(Throwable param0) { super(param0) } public MyException(String param0, Throwable param1) { super(param0, param1) } public MyException(String param0) { super(param0) } public MyException() { super() } @groovy.transform.InheritConstructors class MyException extends Exception { }
  35. 35. @Lazy ▷ Lazy initialization of fields ▷ Useful when creating expensive resources ▷ Effective Java item 71
  36. 36. class SomeBean { @Lazy LinkedList myField }
  37. 37. class SomeBean { @Lazy LinkedList myField } public LinkedList getMyField() { if ($myField != null) { $myField } else { $myField = new LinkedList() } }
  38. 38. @Sortable ▷ Comparable interface ▷ compareTo method natural order ▷ N methods returning comparators ▷ Effective Java item 12
  39. 39. @groovy.transform.Sortable class User { String name Integer age Integer born }
  40. 40. public int compareTo(User other) { if (this.is(other)) return 0 Integer value = 0 value = this.name <=> other.name if (value != 0) return value value = this.age <=> other.age if (value != 0) return value value = this.born <=> other.born if (value != 0) return value return 0 } private static class User$NameComparator extends AbstractComparator<User> { public int compare(User arg0, User arg1) { if (arg0 == arg1) return 0 if (arg0 != null && arg1 == null) return -1 if (arg0 == null && arg1 != null) return 1 return arg0.name <=> arg1.name } } private static class User$AgeComparator extends AbstractComparator<User> { ... } @groovy.transform.Sortable class User { String name Integer age Integer born }
  41. 41. @groovy.transform.Sortable class User { String name Integer age Integer born }
  42. 42. def users = [ new User(name: 'Mary', age: 15, born: 2000), new User(name: 'Peter', age: 44, born: 1970), new User(name: 'John', age: 35, born: 1979), ] @groovy.transform.Sortable class User { String name Integer age Integer born }
  43. 43. assert users.sort(false, User.comparatorByName())*.name == ['John', 'Mary', 'Peter'] assert users.sort(false, User.comparatorByAge())*.born == [2000, 1979, 1970] def users = [ new User(name: 'Mary', age: 15, born: 2000), new User(name: 'Peter', age: 44, born: 1970), new User(name: 'John', age: 35, born: 1979), ] @groovy.transform.Sortable class User { String name Integer age Integer born }
  44. 44. @Sortable ▷ includes, excludes @groovy.transform.Sortable(excludes = 'age') class User { String name Integer age Integer born } def users = [ new User(name: 'Mary', age: 15, born: 2000), new User(name: 'Peter', age: 44, born: 1970), new User(name: 'John', age: 35, born: 1979), ] assert users.sort(false, User.comparatorByName())*.name == ['John', 'Mary', 'Peter'] assert users.sort(false, User.comparatorByAge())*.born == [2000, 1979, 1970]
  45. 45. @Builder ▷ Create fluent API calls ▷ Multiple building strategies ▷ Multiple configuration options: builder name, prefix, excludes, includes,... ▷ Effective Java item 2
  46. 46. @groovy.transform.builder.Builder class User { String name Integer age Integer born }
  47. 47. @groovy.transform.builder.Builder class User { String name Integer age Integer born } def u = User.builder() .name('Iván') .age(37) .born(1979) .build() assert u.name == 'Iván' assert u.age == 37 assert u.born == 1979
  48. 48. public static class User$UserBuilder extends Object { private String name private Integer age private Integer born public User$UserBuilder() { } public User$UserBuilder name(String name) { this.name = name return this } public User$UserBuilder age(Integer age) { this.age = age return this } public User$UserBuilder born(Integer born) { this.born = born return this } public User build() { User _theUser = new User() _theUser.name = name _theUser.age = age _theUser.born = born return _theUser } } @groovy.transform.builder.Builder class User { String name Integer age Integer born } def u = User.builder() .name('Iván') .age(37) .born(1979) .build() assert u.name == 'Iván' assert u.age == 37 assert u.born == 1979
  49. 49. Class design
  50. 50. @Delegate ▷ Implements delegation design pattern ▷ Delegate calls on object to method on delegated properties ▷ All public methods are delegated
  51. 51. import java.time.LocalDate class Conference { @groovy.lang.Delegate LocalDate when String name }
  52. 52. import java.time.LocalDate class Conference { @groovy.lang.Delegate LocalDate when String name } def greach = new Conference(name: 'Greach', when: LocalDate.of(2017, 03, 31)) def gr8conf = new Conference(name: 'GR8Conf' when: LocalDate.of(2017, 05, 31))
  53. 53. def greach = new Conference(name: 'Greach', when: LocalDate.of(2017, 03, 31)) def gr8conf = new Conference(name: 'GR8Conf' when: LocalDate.of(2017, 05, 31)) assert greach.isBefore(gr8conf) import java.time.LocalDate class Conference { @groovy.lang.Delegate LocalDate when String name }
  54. 54. class Conference { ... public boolean isAfter(ChronoLocalDate param0) { when.isAfter(param0) } public boolean isBefore(ChronoLocalDate param0) { when.isBefore(param0) } ... } def greach = new Conference(name: 'Greach', when: LocalDate.of(2017, 03, 31)) def gr8conf = new Conference(name: 'GR8Conf' when: LocalDate.of(2017, 05, 31)) assert greach.isBefore(gr8conf) import java.time.LocalDate class Conference { @groovy.lang.Delegate LocalDate when String name }
  55. 55. @Immutable ▷ Create immutable classes ▷ Effective Java item 15 ▷ Rules for immutability
  56. 56. @groovy.transform.Immutable class User { String name Integer age } def u = new User(name: 'Iván', age: 37)
  57. 57. // This does not compile // You are not allowed to overwrite // the final class 'User'. class Admin extends User { } @groovy.transform.Immutable class User { String name Integer age } def u = new User(name: 'Iván', age: 37)
  58. 58. @groovy.transform.Immutable class User { String name Integer age } def u = new User(name: 'Iván', age: 37) try { u.name = 'John' } catch (ReadOnlyPropertyException e) { println e } // This does not compile // You are not allowed to overwrite // the final class 'User'. class Admin extends User { }
  59. 59. @Memoized ▷ Cache the result of a method
  60. 60. @Memoized ▷ Cache the result of a method @groovy.transform.Memoized Long fibonacci(Integer n) { if (n < 2) return 1 else return fibonacci(n-1) + fibonacci(n-2) } fibonacci(300)
  61. 61. @Memoized ▷ Cache the result of a method @groovy.transform.Memoized Long fibonacci(Integer n) { if (n < 2) return 1 else return fibonacci(n-1) + fibonacci(n-2) } fibonacci(300) @groovy.transform.Memoized User getUserInfo(Long userId) { // Expensive repetitive // network operation }
  62. 62. Logging improvements
  63. 63. @Log, @Log4j, @Log4j2, @Slf4j ▷ Static final field for the logger
  64. 64. @Log, @Log4j, @Log4j2, @Slf4j @groovy.util.logging.Log4j class MyClass { void method() { log.debug "My debug message" } } ▷ Static final field for the logger
  65. 65. @Log, @Log4j, @Log4j2, @Slf4j @groovy.util.logging.Log4j class MyClass { void method() { log.debug "My debug message" } } ▷ Static final field for the logger class MyClass { private static final Logger log = Logger.getLogger(Saludador.name) void method() { if (log.isLoggable(Level.DEBUG)) { log.debug "My debug message" } } }
  66. 66. Declarative concurrency
  67. 67. Declarative concurrency ▷ @Synchronized ▷ @WithReadLock ▷ @WithWriteLock
  68. 68. Cloning and externalizing
  69. 69. Cloning and externalizing ▷ @AutoClone ▷ @AutoExternalize
  70. 70. Safe scripting
  71. 71. Safe scripting ▷ @ThreadInterrupt ▷ @TimedInterrupt ▷ @ConditionalInterrupt
  72. 72. Compiler directives
  73. 73. Compiler directives ▷ @TypeChecked ▷ @CompileStatic ▷ @CompileDynamic ▷ @AnnotationCollector
  74. 74. Dependencies handling
  75. 75. @Grab ▷ Grape dependency manager
  76. 76. @Grab ▷ Grape dependency manager @Grab(group='org.springframework', module='spring-orm', version='3.2.5.RELEASE') import org.springframework.jdbc.core.JdbcTemplate // or @Grab('org.springframework:spring-orm:3.2.5.RELEASE') import org.springframework.jdbc.core.JdbcTemplate
  77. 77. @GrabResolver ▷ Grape dependency manager @Grab(group='org.springframework', module='spring-orm', version='3.2.5.RELEASE') import org.springframework.jdbc.core.JdbcTemplate // or @Grab('org.springframework:spring-orm:3.2.5.RELEASE') import org.springframework.jdbc.core.JdbcTemplate @GrabResolver(name='restlet', root='http://maven.restlet.org/') @Grab(group='org.restlet', module='org.restlet', version='1.1.6')
  78. 78. @GrabExclude ▷ Grape dependency manager @Grab(group='org.springframework', module='spring-orm', version='3.2.5.RELEASE') import org.springframework.jdbc.core.JdbcTemplate // or @Grab('org.springframework:spring-orm:3.2.5.RELEASE') import org.springframework.jdbc.core.JdbcTemplate @GrabResolver(name='restlet', root='http://maven.restlet.org/') @Grab(group='org.restlet', module='org.restlet', version='1.1.6') @Grab('net.sourceforge.htmlunit:htmlunit:2.8') @GrabExclude('xml-apis:xml-apis')
  79. 79. Bonus
  80. 80. @Pokemon class Foo { void doStuff() { try { // Some code that can throw // different exceptions } catch (e) { // Gotta catch’em all } } }
  81. 81. @Pokemon class Foo { void doStuff() { try { // Some code that can throws // different exceptions } catch (e) { // Gotta catch’em all } } } class Foo { @com.github.danveloper.ast.Pokemon void doStuff() { // code } }
  82. 82. 3. Demo
  83. 83. 4. Summary
  84. 84. “ The best code is not code at all
  85. 85. Thanks! Any questions? @ilopmar lopez.ivan@gmail.com https://github.com/ilopmar Iván López

×