What makes Groovy Groovy - Guillaume Laforge (Pivotal)

1,531 views
1,417 views

Published on

Presented at JAX London 2013

Groovy is not a newcomer to the arena of alternative languages for the JVM. With over 1.7 million downloads a year, it's clearly ahead of the pack. But what makes it a great choice for your projects?

- a flat learning curve for Java developers
- its seamless Java integration where you can mix & mash Groovy & Java together
- a malleable & concise syntax fit for Domain-Specific Languages
- an interesting take on type safety
- its rich ecosystem of projects: Grails, Gradle, GPars, Spock, Griffon, Geb...

0 Comments
14 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
1,531
On SlideShare
0
From Embeds
0
Number of Embeds
1
Actions
Shares
0
Downloads
138
Comments
0
Likes
14
Embeds 0
No embeds

No notes for slide

What makes Groovy Groovy - Guillaume Laforge (Pivotal)

  1. 1. What makes Groovy groovy? Guillaume Laforge 
 @glaforge
  2. 2. Guillaume Laforge Groovy project lead at . ! @glaforge http://glaforge.appspot.com
  3. 3. 1 t r a P The Groovy vision
  4. 4. Simplify the life of (Java) developers
  5. 5. Groovy as a Java superset
  6. 6. Groovy as a Java superset It’s so easy to learn!
  7. 7. class  MathSpec  extends  Specification  {          def  "maximum  of  two  numbers"()  {              expect:                  Math.max(a,  b)  ==  c   !            where:                  a  |  b  ||  c                  1  |  3  ||  3                  7  |  4  ||  4                  0  |  0  ||  0          }   }
  8. 8. As safe and fast as Java with static type checking & compilation
  9. 9. As safe and fast as Java with static type checking & compilation
  10. 10. new  MarkupBuilder().html  {          head  {                  title  "The  Script  Bowl"          }   !        body  {                  div(class:  "banner")  {                          p  "Groovy  rocks!"                  }          }   }
  11. 11. move  forward  at  3.km/h Expressive, Concise, Readable
  12. 12. Speaking of conciseness... A full Spring app in the span of a tweet! @RestController   class  App  {          @RequestMapping("/")          String  home()  {  "Hello  World!"  }   }
  13. 13. 1.7 million 
 downloads
 per year
  14. 14. Great for scripting
  15. 15. Great for scripting Fit for DomainSpecific Languages
  16. 16. Great for scripting Fit for DomainSpecific Languages Most seamless integration & interoperability wih java!
  17. 17. Yup, we’re all using Groovy!
  18. 18. 2 t r a P Cool Groovy gems
  19. 19. Most Java code is also valid Groovy code!
  20. 20. Most Java code is also valid Groovy code! Any Java developer is a Groovy developer!
  21. 21. Flat learning curve
  22. 22. Flat learning curve Easy to learn
  23. 23. Scripts versus Classes public  class  Main  {          public  static  void  main(String[]  args)  {                  System.out.println("Hello");          }   } vs !18
  24. 24. Scripts versus Classes public  class  Main  {          public  static  void  main(String[]  args)  {                  System.out.println("Hello");          }   } vs println  "Hello" !18
  25. 25. Optional
  26. 26. Semicolons Optional
  27. 27. Semicolons Parentheses Optional
  28. 28. Semicolons Parentheses Optional return keyword
  29. 29. Semicolons Parentheses Optional return keyword public keyword
  30. 30. Semicolons Parentheses Optional Typing! return keyword public keyword
  31. 31. Optional... public  class  Greeter  {          private  String  owner;   !        public  String  getOwner()  {                  return  owner;          }   !        public  void  setOwner(String  owner)  {                  this.owner  =  owner;          }   !        public  String  greet(String  name)  {                  return  "Hello  "  +  name  +  ",  I  am  "  +  owner;          }   }   ! Greeter  greeter  =  new  Greeter();   greeter.setOwner("Guillaume");   ! System.out.println(greeter.greet("Marion")); !20
  32. 32. Optional... Semicolons public  class  Greeter  {          private  String  owner;   !        public  String  getOwner()  {                  return  owner;          }   !        public  void  setOwner(String  owner)  {                  this.owner  =  owner;          }   !        public  String  greet(String  name)  {                  return  "Hello  "  +  name  +  ",  I  am  "  +  owner;          }   }   ! Greeter  greeter  =  new  Greeter();   greeter.setOwner("Guillaume");   ! System.out.println(greeter.greet("Marion")); !20
  33. 33. Optional... public  class  Greeter  {          private  String  owner   !        public  String  getOwner()  {                  return  owner          }   !        public  void  setOwner(String  owner)  {                  this.owner  =  owner          }   !        public  String  greet(String  name)  {                  return  "Hello  "  +  name  +  ",  I  am  "  +  owner          }   }   ! Greeter  greeter  =  new  Greeter()   greeter.setOwner("Guillaume")   ! System.out.println(greeter.greet("Marion")) !21
  34. 34. Optional... public  class  Greeter  {          private  String  owner   !        public  String  getOwner()  {                  return  owner          }   !        public  void  setOwner(String  owner)  {                  this.owner  =  owner          }   !        public  String  greet(String  name)  {                  return  "Hello  "  +  name  +  ",  I  am  "  +  owner          }   }   ! Greeter  greeter  =  new  Greeter()   greeter.setOwner("Guillaume")   ! System.out.println(greeter.greet("Marion")) !22
  35. 35. Optional... public  class  Greeter  {          private  String  owner   !        public  String  getOwner()  {                  return  owner          }   !        public  void  setOwner(String  owner)  {                  this.owner  =  owner          }   !        public  String  greet(String  name)  {                  return  "Hello  "  +  name  +  ",  I  am  "  +  owner          }   }   ! Greeter  greeter  =  new  Greeter()   greeter.setOwner("Guillaume")   ! System.out.println(greeter.greet("Marion")) Parentheses !22
  36. 36. Optional... public  class  Greeter  {          private  String  owner   !        public  String  getOwner()  {                  return  owner          }   !        public  void  setOwner(String  owner)  {                  this.owner  =  owner          }   !        public  String  greet(String  name)  {                  return  "Hello  "  +  name  +  ",  I  am  "  +  owner          }   }   ! Greeter  greeter  =  new  Greeter()   greeter.setOwner  "Guillaume"   ! System.out.println  greeter.greet("Marion") !23
  37. 37. Optional... public  class  Greeter  {          private  String  owner   !        public  String  getOwner()  {                  return  owner          }   !        public  void  setOwner(String  owner)  {                  this.owner  =  owner          }   !        public  String  greet(String  name)  {                  return  "Hello  "  +  name  +  ",  I  am  "  +  owner          }   }   ! Greeter  greeter  =  new  Greeter()   greeter.setOwner  "Guillaume"   ! System.out.println  greeter.greet("Marion") return keyword !23
  38. 38. Optional... public  class  Greeter  {          private  String  owner   !        public  String  getOwner()  {                                owner          }   !        public  void  setOwner(String  owner)  {                  this.owner  =  owner          }   !        public  String  greet(String  name)  {                                "Hello  "  +  name  +  ",  I  am  "  +  owner          }   }   ! Greeter  greeter  =  new  Greeter()   greeter.setOwner  "Guillaume"   ! System.out.println  greeter.greet("Marion") !24
  39. 39. Optional... public  class  Greeter  {          private  String  owner   !        public  String  getOwner()  {                                owner          }   !        public  void  setOwner(String  owner)  {                  this.owner  =  owner          }   !        public  String  greet(String  name)  {                                "Hello  "  +  name  +  ",  I  am  "  +  owner          }   }   ! Greeter  greeter  =  new  Greeter()   greeter.setOwner  "Guillaume"   ! System.out.println  greeter.greet("Marion") public keyword !24
  40. 40. Optional...              class  Greeter  {          private  String  owner   !                      String  getOwner()  {                                owner          }   !                      void  setOwner(String  owner)  {                  this.owner  =  owner          }   !                      String  greet(String  name)  {                                "Hello  "  +  name  +  ",  I  am  "  +  owner          }   }   ! Greeter  greeter  =  new  Greeter()   greeter.setOwner  "Guillaume"   ! System.out.println  greeter.greet("Marion") !25
  41. 41. Optional...              class  Greeter  {          private  String  owner   !                      String  getOwner()  {                                owner          }   !                      void  setOwner(String  owner)  {                  this.owner  =  owner          }   !                      String  greet(String  name)  {                                "Hello  "  +  name  +  ",  I  am  "  +  owner          }   }   ! Greeter  greeter  =  new  Greeter()   greeter.setOwner  "Guillaume"   ! System.out.println  greeter.greet("Marion") optional typing !25
  42. 42. Optional...              class  Greeter  {          private  String  owner   !                      String  getOwner()  {                                owner          }   !                      void  setOwner(String  owner)  {                  this.owner  =  owner          }   !                      String  greet(String  name)  {                                "Hello  "  +  name  +  ",  I  am  "  +  owner          }   }   ! def          greeter  =  new  Greeter()   greeter.setOwner  "Guillaume"   ! System.out.println  greeter.greet("Marion") !26
  43. 43. Optional...              class  Greeter  {          private  String  owner   !                      String  getOwner()  {                                owner          }   !                      void  setOwner(String  owner)  {                  this.owner  =  owner          }   !                      String  greet(String  name)  {                                "Hello  "  +  name  +  ",  I  am  "  +  owner          }   }   ! def          greeter  =  new  Greeter()   greeter.setOwner  "Guillaume"   ! System.out.println  greeter.greet("Marion") handy println shortcut !26
  44. 44. Optional...              class  Greeter  {          private  String  owner   !                      String  getOwner()  {                                owner          }   !                      void  setOwner(String  owner)  {                  this.owner  =  owner          }   !                      String  greet(String  name)  {                                "Hello  "  +  name  +  ",  I  am  "  +  owner          }   }   ! def          greeter  =  new  Greeter()   greeter.setOwner  "Guillaume"   !                      println  greeter.greet("Marion") !27
  45. 45. Optional... verbose Java properties!              class  Greeter  {          private  String  owner   !                      String  getOwner()  {                                owner          }   !                      void  setOwner(String  owner)  {                  this.owner  =  owner          }   !                      String  greet(String  name)  {                                "Hello  "  +  name  +  ",  I  am  "  +  owner          }   }   ! def          greeter  =  new  Greeter()   greeter.setOwner  "Guillaume"   !                      println  greeter.greet("Marion") !27
  46. 46. Optional...              class  Greeter  {                          String  owner   ! ! ! ! ! ! ! ! !                      String  greet(String  name)  {                                "Hello  "  +  name  +  ",  I  am  "  +  owner          }   }   ! def          greeter  =  new  Greeter()   greeter.setOwner  "Guillaume"   !                      println  greeter.greet("Marion") !28
  47. 47. Optional...              class  Greeter  {                          String  owner   ! ! ! ! ! ! ! ! !                      String  greet(String  name)  {                                "Hello  "  +  name  +  ",  I  am  "  +  owner          }   }   ! def          greeter  =  new  Greeter()   greeter.setOwner  "Guillaume"   !                      println  greeter.greet("Marion") Property notation !28
  48. 48. Optional...              class  Greeter  {                          String  owner   ! ! ! ! ! ! ! ! !                      String  greet(String  name)  {                                "Hello  "  +  name  +  ",  I  am  "  +  owner          }   }   ! def          greeter  =  new  Greeter()   greeter.owner        "Guillaume"   !                      println  greeter.greet("Marion") !29
  49. 49. Optional...              class  Greeter  {                          String  owner   ! ! ! ! ! ! ! ! !                      String  greet(String  name)  {                                "Hello  "  +  name  +  ",  I  am  "  +  owner          }   }   ! def          greeter  =  new  Greeter()   greeter.owner        "Guillaume"   !                      println  greeter.greet("Marion") Named argument constructor !29
  50. 50. Optional...              class  Greeter  {                          String  owner   ! ! ! ! ! ! ! ! !                      String  greet(String  name)  {                                "Hello  "  +  name  +  ",  I  am  "  +  owner          }   }   ! def          greeter  =  new  Greeter(owner:  "Guillaume")   ! !                      println  greeter.greet("Marion") !30
  51. 51. Optional...              class  Greeter  {                          String  owner   ! ! ! ! ! ! ! ! !                      String  greet(String  name)  {                                "Hello  "  +  name  +  ",  I  am  "  +  owner          }   }   ! def          greeter  =  new  Greeter(owner:  "Guillaume")   ! !                      println  greeter.greet("Marion") Interpolated strings! (aka GStrings) !30
  52. 52. Optional...              class  Greeter  {                          String  owner   ! ! ! ! ! ! ! ! !                      String  greet(String  name)  {                                "Hello  ${name},  I  am  ${owner}"          }   }   ! def          greeter  =  new  Greeter(owner:  "Guillaume")   ! !                      println  greeter.greet("Marion") !31
  53. 53. Optional...              class  Greeter  {                          String  owner   ! ! ! ! ! ! ! ! !                      String  greet(String  name)  {                                "Hello  ${name},  I  am  ${owner}"          }   }   ! def          greeter  =  new  Greeter(owner:  "Guillaume")   ! !                      println  greeter.greet("Marion") Let’s reformat that mess of whitespace! !31
  54. 54. Optional... class  Greeter  {          String  owner   !        String  greet(String  name)  {                "Hello  ${name},  I  am  ${owner}"          }   }   ! def  greeter  =  new  Greeter(owner:  "Guillaume")   ! println  greeter.greet("Marion") !32
  55. 55. Optional... public  class  Greeter  {          private  String  owner;   !        public  String  getOwner()  {   class  Greeter  {                  return  owner;          String  owner          }   ! !        String  greet(String  name)  {          public  void  setOwner(String  owner)  {                "Hello  ${name},  I  am  ${owner}"                  this.owner  =  owner;          }          }   }   ! !        public  String  greet(String  name)  {   def  greeter  =  new  Greeter(owner:  "Guillaume")                  return  "Hello  "  +  name  +  ",  I  am  "  +  owner;   !        }   println  greeter.greet("Marion") }   ! Greeter  greeter  =  new  Greeter();   greeter.setOwner("Guillaume");   ! System.out.println(greeter.greet("Marion")); !32
  56. 56. Optional... class  Greeter  {          String  owner   !        String  greet(String  name)  {                "Hello  ${name},  I  am  ${owner}"          }   }   ! def  greeter  =  new  Greeter(owner:  "Guillaume")   ! println  greeter.greet("Marion") !32
  57. 57. Native syntax constructs //  closures   def  adder  =  {  a,  b  -­‐>  a  +  b  }   ! //  lists   def  list  =  [1,  2,  3,  4,  5]   ! //  maps   def  map  =  [a:  1,  b:  2,  c:  3]   ! //  regular  expressions   def  regex  =  ~/.*foo.*/   ! //  ranges   def  range  128..255 !33
  58. 58. Closures — the basics def  adder  =  {  a,  b  -­‐>  a  +  b  }   ! assert  adder(1,  2)  ==  3   assert  adder('a',  'b')  ==  'ab' !34
  59. 59. Closures — the basics Closure parameters def  adder  =  {  a,  b  -­‐>  a  +  b  }   ! assert  adder(1,  2)  ==  3   assert  adder('a',  'b')  ==  'ab' !34
  60. 60. Closures — the basics Assign a function into a variable Closure parameters def  adder  =  {  a,  b  -­‐>  a  +  b  }   ! assert  adder(1,  2)  ==  3   assert  adder('a',  'b')  ==  'ab' !34
  61. 61. Closures — the basics Assign a function into a variable Closure parameters def  adder  =  {  a,  b  -­‐>  a  +  b  }   ! assert  adder(1,  2)  ==  3   assert  adder('a',  'b')  ==  'ab' Short form of: adder.call(‘a’, ‘b’) !34
  62. 62. Closures — the basics Assign a function into a variable Closure parameters def  adder  =  {  a,  b  -­‐>  a  +  b  }   ! assert  adder(1,  2)  ==  3   assert  adder('a',  'b')  ==  'ab' Short form of: adder.call(‘a’, ‘b’) !34 Genericity with duck typing & operator overloading
  63. 63. Closures — explicit type ! def  intAdder  =  {  int  a,  int  b  -­‐>  a  +  b  }   !35
  64. 64. Closures — explicit type Be explicit about the types ! def  intAdder  =  {  int  a,  int  b  -­‐>  a  +  b  }   !35
  65. 65. Closures — implicit parameter def  doubler  =  {  it  *  2  }   ! assert  doubler(3)  ==  6   assert  doubler('a')  ==  'aa' !36
  66. 66. Closures — implicit parameter Implicit parameter def  doubler  =  {  it  *  2  }   ! assert  doubler(3)  ==  6   assert  doubler('a')  ==  'aa' !36
  67. 67. Closures — implicit parameter Implicit parameter def  doubler  =  {  it  *  2  }   ! assert  doubler(3)  ==  6   assert  doubler('a')  ==  'aa' Multiply also defined on strings !36
  68. 68. Closures — variable arguments def  sum  =  {  ...  elements  -­‐>  
                                elements.sum()  }   ! assert  sum(1,  2)  ==  3   assert  sum('a',  'b',  'c')  ==  'abc' !37
  69. 69. Closures — variable arguments Variable number of arguments def  sum  =  {  ...  elements  -­‐>  
                                elements.sum()  }   ! assert  sum(1,  2)  ==  3   assert  sum('a',  'b',  'c')  ==  'abc' !37
  70. 70. Closures — variable arguments You can specify the type: int... Variable number of arguments def  sum  =  {  ...  elements  -­‐>  
                                elements.sum()  }   ! assert  sum(1,  2)  ==  3   assert  sum('a',  'b',  'c')  ==  'abc' !37
  71. 71. Closures — default values def  mult  =  {  int  a,  int  b  =  10  -­‐>  a  *  b  }   ! assert  mult(2,  3)  ==  6   assert  mult(5)  ==  50 !38
  72. 72. Closures — default values Default value def  mult  =  {  int  a,  int  b  =  10  -­‐>  a  *  b  }   ! assert  mult(2,  3)  ==  6   assert  mult(5)  ==  50 !38
  73. 73. Closures — default values Default value def  mult  =  {  int  a,  int  b  =  10  -­‐>  a  *  b  }   ! assert  mult(2,  3)  ==  6   assert  mult(5)  ==  50 !38 Provided value for b
  74. 74. Closures — default values Default value def  mult  =  {  int  a,  int  b  =  10  -­‐>  a  *  b  }   ! assert  mult(2,  3)  ==  6   assert  mult(5)  ==  50 Default value used for b !38 Provided value for b
  75. 75. Closures — methods as functions def  logBase10  =  Math.&log10   def  printer  =  System.out.&println   ! assert  logBase10(10)  ==  1   printer  'abc' !39
  76. 76. Closures — methods as functions Turn a method into a closure function def  logBase10  =  Math.&log10   def  printer  =  System.out.&println   ! assert  logBase10(10)  ==  1   printer  'abc' !39
  77. 77. Closures — map / filter / reduce !40
  78. 78. Closures — map / filter / reduce @groovy.transform.Immutable
 class  Person  {
        String  name
        int  age
 } !40
  79. 79. Closures — map / filter / reduce @groovy.transform.Immutable
 class  Person  {
        String  name
        int  age
 } !40
  80. 80. Closures — map / filter / reduce @groovy.transform.Immutable
 class  Person  {
        String  name
        int  age
 } def  persons  =  [
        new  Person('Guillaume',  36),
        new  Person('Marion',  5),
        new  Person('Erine',  1)
 ] !40
  81. 81. Closures — map / filter / reduce @groovy.transform.Immutable
 class  Person  {
        String  name
        int  age
 } def  persons  =  [
        new  Person('Guillaume',  36),
        new  Person('Marion',  5),
        new  Person('Erine',  1)
 ] !40
  82. 82. Closures — map / filter / reduce @groovy.transform.Immutable
 class  Person  {
        String  name
        int  age
 } def  persons  =  [
        new  Person('Guillaume',  36),
        new  Person('Marion',  5),
        new  Person('Erine',  1)
 ] def  names  =
        persons.findAll  {  it.age  <  18  }
                      .collect  {  it.name.toUpperCase()  }
                      .sort()
                      .join(',  ') !40
  83. 83. Closures — map / filter / reduce @groovy.transform.Immutable
 class  Person  {
        String  name
        int  age
 } def  persons  =  [
        new  Person('Guillaume',  36),
        new  Person('Marion',  5),
        new  Person('Erine',  1)
 ] def  names  =
        persons.findAll  {  it.age  <  18  }
                      .collect  {  it.name.toUpperCase()  }
                      .sort()
                      .join(',  ') !40
  84. 84. Closures — map / filter / reduce @groovy.transform.Immutable
 class  Person  {
        String  name
        int  age
 } def  persons  =  [
        new  Person('Guillaume',  36),
        new  Person('Marion',  5),
        new  Person('Erine',  1)
 ] def  names  =
        persons.findAll  {  it.age  <  18  }
                      .collect  {  it.name.toUpperCase()  }
                      .sort()
                      .join(',  ') assert  names  ==  "ERINE,  MARION" !40
  85. 85. Closures — map / filter / reduce @groovy.transform.Immutable
 class  Person  {
        String  name
        int  age
 } def  persons  =  [
        new  Person('Guillaume',  36),
        new  Person('Marion',  5),
        new  Person('Erine',  1)
 ] find/findAll, inject, collect, flatten, min/max, unique, reverse, collate, groupBy, any/ every, head/tail/last, count/ countBy, combinations/ permutations/subsequences/ transpose, withDefault/ withLazyDefault def  names  =
        persons.findAll  {  it.age  <  18  }
                      .collect  {  it.name.toUpperCase()  }
                      .sort()
                      .join(',  ') assert  names  ==  "ERINE,  MARION" !40
  86. 86. Closures — resource handling new  File('bible.txt').withReader  {  r  -­‐>          new  File('out.txt').withWriter  {  w  -­‐>                  r.eachLine  {  line  -­‐>                          if  (line.contains('Groovy'))                                  w  <<  line.toUpperCase()                  }          }   } !41
  87. 87. Closures — resource handling Take care of properly opening / closing resources new  File('bible.txt').withReader  {  r  -­‐>          new  File('out.txt').withWriter  {  w  -­‐>                  r.eachLine  {  line  -­‐>                          if  (line.contains('Groovy'))                                  w  <<  line.toUpperCase()                  }          }   } !41
  88. 88. Closures — custom control structures void  unless(boolean  cond,  Closure  c)  {          if  (!cond)  c()   }   ! unless  (10  <  9)  {          println  "less"   } !42
  89. 89. Closures — custom control structures Closure as last argument void  unless(boolean  cond,  Closure  c)  {          if  (!cond)  c()   }   ! unless  (10  <  9)  {          println  "less"   } !42
  90. 90. Closures — custom control structures Closure as last argument void  unless(boolean  cond,  Closure  c)  {          if  (!cond)  c()   }   ! unless  (10  <  9)  {          println  "less"   } !42 Equivalent to: unless(10<9, {...})
  91. 91. Lists def  list  =  ['a',  'b',  'c']   ! list  <<  'd'   assert  list.contains('d')   ! assert  list.findAll  {  it.startsWith  'a'  }.size()  ==  1   assert  list.collect  {  it.toUpperCase()  }  
                                                              ==  ['A',  'B',  'C',  'D']   assert  list.inject('')  {  a,  b  -­‐>  a  +  b  }  ==  'abcd' !43
  92. 92. Lists List definition def  list  =  ['a',  'b',  'c']   ! list  <<  'd'   assert  list.contains('d')   ! assert  list.findAll  {  it.startsWith  'a'  }.size()  ==  1   assert  list.collect  {  it.toUpperCase()  }  
                                                              ==  ['A',  'B',  'C',  'D']   assert  list.inject('')  {  a,  b  -­‐>  a  +  b  }  ==  'abcd' !43
  93. 93. Lists List definition def  list  =  ['a',  'b',  'c']   ! Append an element (operator overloading) list  <<  'd'   assert  list.contains('d')   ! assert  list.findAll  {  it.startsWith  'a'  }.size()  ==  1   assert  list.collect  {  it.toUpperCase()  }  
                                                              ==  ['A',  'B',  'C',  'D']   assert  list.inject('')  {  a,  b  -­‐>  a  +  b  }  ==  'abcd' !43
  94. 94. Lists List definition def  list  =  ['a',  'b',  'c']   ! Append an element (operator overloading) list  <<  'd'   assert  list.contains('d')   ! assert  list.findAll  {  it.startsWith  'a'  }.size()  ==  1   assert  list.collect  {  it.toUpperCase()  }  
                                                              ==  ['A',  'B',  'C',  'D']   assert  list.inject('')  {  a,  b  -­‐>  a  +  b  }  ==  'abcd' Functional-style map / filter / reduce with closures !43
  95. 95. Maps def  map  =  [name:  'Guillaume',  age:  36]   ! map.daughters  =  ['Marion',  'Erine']   ! assert  map['daughters'].contains('Marion') !44
  96. 96. Maps Map definition def  map  =  [name:  'Guillaume',  age:  36]   ! map.daughters  =  ['Marion',  'Erine']   ! assert  map['daughters'].contains('Marion') !44
  97. 97. Maps Map definition def  map  =  [name:  'Guillaume',  age:  36]   ! map.daughters  =  ['Marion',  'Erine']   ! assert  map['daughters'].contains('Marion') Indexed access !44
  98. 98. Maps Map definition def  map  =  [name:  'Guillaume',  age:  36]   ! map.daughters  =  ['Marion',  'Erine']   ! assert  map['daughters'].contains('Marion') Property notation access !44 Indexed access
  99. 99. Regular expressions def  pattern  =  ~/.*foo.*/   ! assert  "Alibaba"  ==~  /.*(ba){2}/   ! def  matcher  =  "Superman"  =~  /([A-­‐Z][a-­‐z]+)man/   assert  matcher[0][0]  ==  'Superman'   assert  matcher[0][1]  ==  'Super'   ! '75001  Paris'.find(/(d{5})s(w)+/)  {  match,  zip,  town  -­‐>          println  "The  Zip  code  of  ${town}  is  ${zip}"   } !45
  100. 100. Regular expressions Pattern def  pattern  =  ~/.*foo.*/   ! assert  "Alibaba"  ==~  /.*(ba){2}/   ! def  matcher  =  "Superman"  =~  /([A-­‐Z][a-­‐z]+)man/   assert  matcher[0][0]  ==  'Superman'   assert  matcher[0][1]  ==  'Super'   ! '75001  Paris'.find(/(d{5})s(w)+/)  {  match,  zip,  town  -­‐>          println  "The  Zip  code  of  ${town}  is  ${zip}"   } !45
  101. 101. Regular expressions Pattern Match def  pattern  =  ~/.*foo.*/   ! assert  "Alibaba"  ==~  /.*(ba){2}/   ! def  matcher  =  "Superman"  =~  /([A-­‐Z][a-­‐z]+)man/   assert  matcher[0][0]  ==  'Superman'   assert  matcher[0][1]  ==  'Super'   ! '75001  Paris'.find(/(d{5})s(w)+/)  {  match,  zip,  town  -­‐>          println  "The  Zip  code  of  ${town}  is  ${zip}"   } !45
  102. 102. Regular expressions Pattern Match def  pattern  =  ~/.*foo.*/   ! assert  "Alibaba"  ==~  /.*(ba){2}/   Find ! def  matcher  =  "Superman"  =~  /([A-­‐Z][a-­‐z]+)man/   assert  matcher[0][0]  ==  'Superman'   assert  matcher[0][1]  ==  'Super'   ! '75001  Paris'.find(/(d{5})s(w)+/)  {  match,  zip,  town  -­‐>          println  "The  Zip  code  of  ${town}  is  ${zip}"   } !45
  103. 103. Regular expressions Pattern Match def  pattern  =  ~/.*foo.*/   ! assert  "Alibaba"  ==~  /.*(ba){2}/   Find ! def  matcher  =  "Superman"  =~  /([A-­‐Z][a-­‐z]+)man/   assert  matcher[0][0]  ==  'Superman'   assert  matcher[0][1]  ==  'Super'   ! '75001  Paris'.find(/(d{5})s(w)+/)  {  match,  zip,  town  -­‐>          println  "The  Zip  code  of  ${town}  is  ${zip}"   } Nice way to decompose the matched regions !45
  104. 104. Ranges def  range  =  'a'..'z'   ! assert  range.contains('m')   assert  range.contains('z')   ! def  exclusive  =  1..<10   ! assert  !exclusive.contains(10)   ! def  reverse  =  10..0   ! assert  reverse[0]  ==  10   assert  reverse[-­‐1]  ==  0 !46
  105. 105. Ranges Range def  range  =  'a'..'z'   ! assert  range.contains('m')   assert  range.contains('z')   ! def  exclusive  =  1..<10   ! assert  !exclusive.contains(10)   ! def  reverse  =  10..0   ! assert  reverse[0]  ==  10   assert  reverse[-­‐1]  ==  0 !46
  106. 106. Ranges Range def  range  =  'a'..'z'   ! assert  range.contains('m')   assert  range.contains('z')   ! def  exclusive  =  1..<10   Excluded upper bound ! assert  !exclusive.contains(10)   ! def  reverse  =  10..0   ! assert  reverse[0]  ==  10   assert  reverse[-­‐1]  ==  0 !46
  107. 107. Ranges Range def  range  =  'a'..'z'   ! assert  range.contains('m')   assert  range.contains('z')   ! def  exclusive  =  1..<10   Excluded upper bound ! assert  !exclusive.contains(10)   ! def  reverse  =  10..0   ! assert  reverse[0]  ==  10   assert  reverse[-­‐1]  ==  0 !46 Reverse range
  108. 108. Ranges Range def  range  =  'a'..'z'   ! assert  range.contains('m')   assert  range.contains('z')   ! def  exclusive  =  1..<10   Excluded upper bound ! assert  !exclusive.contains(10)   ! def  reverse  =  10..0   Reverse range ! assert  reverse[0]  ==  10   assert  reverse[-­‐1]  ==  0 !46 Negative index count from the end
  109. 109. Strings, GStrings, multiline strings def  name  =  'Groovy'   def  tmpl  =  """          Dear  Mr  ${name},          You're  the  winner  of  the  lottery!          Yours  sincerly,          Dave   """   ! assert  tmpl.toString().contains('Groovy') !47
  110. 110. Strings, GStrings, multiline strings Plain java.lang.String def  name  =  'Groovy'   def  tmpl  =  """          Dear  Mr  ${name},          You're  the  winner  of  the  lottery!          Yours  sincerly,          Dave   """   ! assert  tmpl.toString().contains('Groovy') !47
  111. 111. Strings, GStrings, multiline strings Plain java.lang.String def  name  =  'Groovy'   Multiline string with def  tmpl  =  """   expression interpolation        Dear  Mr  ${name},          You're  the  winner  of  the  lottery!          Yours  sincerly,          Dave   """   ! assert  tmpl.toString().contains('Groovy') !47
  112. 112. Surprising numbers... System.out.println(  2.0  -­‐  1.1  ); !48
  113. 113. Surprising numbers... System.out.println(  2.0  -­‐  1.1  ); 0.8999999999999999 !48
  114. 114. Surprising numbers... System.out.println(  2.0  -­‐  1.1  ); 0.8999999999999999 !48
  115. 115. Surprising numbers... System.out.println(  3  /  2  ); !49
  116. 116. Surprising numbers... System.out.println(  3  /  2  ); 1 !49
  117. 117. Surprising numbers... System.out.println(  3  /  2  ); 1 !49
  118. 118. BigDecimal by default! assert  2.0  -­‐  1.1  ==  0.9 assert  3  /  2  ==  1.5 !50
  119. 119. BigDecimal by default! assert  2.0  -­‐  1.1  ==  0.9 assert  3  /  2  ==  1.5 One of the reasons why microbenchmarks sometimes showed Groovy to be slow... !50
  120. 120. BigDecimal by default! assert  2.0  -­‐  1.1  ==  0.9 assert  3  /  2  ==  1.5 One of the reasons why microbenchmarks sometimes showed Groovy to be slow... !50 But you can use doubles & floats for performance, with ‘d’ or ‘f ’ suffixes or with explicit type
  121. 121. Powerful switch / case on steroids
  122. 122. Powerful switch / case on steroids switch(obj)  {          case  123:                  "number  123";              break          case  "abc":              "string  abc";              break          case  String:            "is  a  string";            break          case  [1,  2,  3]:      "in  list";                    break          case  ~/.*o+.*/:      "regex  match";            break          case  {  it  <  3  }:    "closure  criteria";  break          default:                    "unknown"   }
  123. 123. Named arguments move  obj,  x:  3,  y:  4 !52
  124. 124. Named arguments move  obj,  x:  3,  y:  4 Normal argument !52
  125. 125. Named arguments move  obj,  x:  3,  y:  4 Normal argument !52 Named argument
  126. 126. Named arguments move  obj,  x:  3,  y:  4 Normal argument Calls: move(Map m, Object) !52 Named argument
  127. 127. Command chains • Ability to chain method calls 
 without parentheses and dots move  forward  at  3.km/h !53
  128. 128. Command chains • Ability to chain method calls 
 without parentheses and dots move  forward  at  3.km/h Actually equivalent to: move(forward).at(3.getKm().div(h)) !53
  129. 129. Named arguments & command chains check  that:  vodka  tastes  good !54
  130. 130. Named arguments & command chains check  that:  vodka  tastes  good Will call: check(that: vodka).tastes(good) !54
  131. 131. Multiple assignment & destructuring def  (a,  b)  =  ['A',  'B']   ! (a,  b)  =  [b,  a]   ! def  (int  i,  int  j)  =  [1,  2]   ! def  geocode(String  place)  {          return  [45.4,  2.3]   }   ! def  (la,  lo)  =  geocode("Paris")   ! assert  la  ==  45.4  &&  lo  ==  2.3 !55
  132. 132. Multiple assignment & destructuring def  (a,  b)  =  ['A',  'B']   ! (a,  b)  =  [b,  a]   Classic « swap » ! def  (int  i,  int  j)  =  [1,  2]   ! def  geocode(String  place)  {          return  [45.4,  2.3]   }   ! def  (la,  lo)  =  geocode("Paris")   ! assert  la  ==  45.4  &&  lo  ==  2.3 !55
  133. 133. Multiple assignment & destructuring def  (a,  b)  =  ['A',  'B']   ! With types (a,  b)  =  [b,  a]   Classic « swap » ! def  (int  i,  int  j)  =  [1,  2]   ! def  geocode(String  place)  {          return  [45.4,  2.3]   }   ! def  (la,  lo)  =  geocode("Paris")   ! assert  la  ==  45.4  &&  lo  ==  2.3 !55
  134. 134. Multiple assignment & destructuring def  (a,  b)  =  ['A',  'B']   ! With types (a,  b)  =  [b,  a]   Classic « swap » ! def  (int  i,  int  j)  =  [1,  2]   Method returning a list ! def  geocode(String  place)  {          return  [45.4,  2.3]   }   ! def  (la,  lo)  =  geocode("Paris")   ! assert  la  ==  45.4  &&  lo  ==  2.3 !55
  135. 135. Multiple assignment & destructuring def  (a,  b)  =  ['A',  'B']   ! With types (a,  b)  =  [b,  a]   Classic « swap » ! def  (int  i,  int  j)  =  [1,  2]   Method returning a list ! def  geocode(String  place)  {          return  [45.4,  2.3]   }   Destructuring ! def  (la,  lo)  =  geocode("Paris")   ! assert  la  ==  45.4  &&  lo  ==  2.3 !55
  136. 136. Multiple assignment and destructuring class  Point  {          double  x,  y   !        double  getAt(int  idx)  {                  if  (idx  ==  0)  x                  else  if  (idx  ==  1)  y                  else  throw  new  Exception("Wrong  index")          }   }   ! def  (x,  y)  =  new  Point(x:  48.3,  y:  3.5)   ! assert  x  ==  48.3  &&  y  ==  3.5 !56
  137. 137. Multiple assignment and destructuring class  Point  {          double  x,  y   Method signature convention: getAt(int) !        double  getAt(int  idx)  {                  if  (idx  ==  0)  x                  else  if  (idx  ==  1)  y                  else  throw  new  Exception("Wrong  index")          }   }   ! def  (x,  y)  =  new  Point(x:  48.3,  y:  3.5)   ! assert  x  ==  48.3  &&  y  ==  3.5 !56
  138. 138. Multiple assignment and destructuring class  Point  {          double  x,  y   Method signature convention: getAt(int) !        double  getAt(int  idx)  {                  if  (idx  ==  0)  x                  else  if  (idx  ==  1)  y                  else  throw  new  Exception("Wrong  index")          }   }   Transparent destructuring ! def  (x,  y)  =  new  Point(x:  48.3,  y:  3.5)   ! assert  x  ==  48.3  &&  y  ==  3.5 !56
  139. 139. Builders — JSON builder import  groovy.json.*   ! def  json  =  new  JsonBuilder()   json.person  {          name  'Guillaume'          age  36          daughters  'Marion',  'Erine'          address  {                  street  '1  Main  Street'                  zip  75001                  city  'Paris'          }   } !57
  140. 140. Builders — JSON builder import  groovy.json.*   Hierarchical data def  json  =  new  JsonBuilder()   representation ! json.person  {          name  'Guillaume'          age  36          daughters  'Marion',  'Erine'          address  {                  street  '1  Main  Street'                  zip  75001                  city  'Paris'          }   } !57
  141. 141. Builders — JSON builder import  groovy.json.*   Hierarchical data def  json  =  new  JsonBuilder()   representation ! json.person  {          name  'Guillaume'   Closure blocks        age  36   delimiting        daughters  'Marion',  'Erine'  the structure        address  {                  street  '1  Main  Street'                  zip  75001                  city  'Paris'          }   } !57
  142. 142. Builders — JSON builder import  groovy.json.*   Hierarchical data { "person": { def  json  =  new  JsonBuilder()   representation ! "name": "Guillaume", json.person  {   "age": 36,        name  'Guillaume'   [ "daughters": Closure blocks "Marion",        age  36   "Erine" delimiting        daughters  'Marion',  'Erine'  the ], structure "address": {        address  {   "street": "1 Main Street",                street  '1  Main  Street'   "zip": 75001, "city":                zip  75001   "Paris" }                city  'Paris'   }        }   } } !57
  143. 143. GPath expressions • GPath expressions are like XPath 
 but for an object graph import  groovy.json.*   ! def  url  =  
    "https://api.github.com/repos/groovy/groovy-­‐core/commits"   ! def  commits  =  new  JsonSlurper().parseText(url.toURL().text)   ! assert  commits[0].commit.author.name  ==  'Cedric  Champeau' !58
  144. 144. GPath expressions • GPath expressions are like XPath 
 but for an object graph import  groovy.json.*   ! def  url  =  
    "https://api.github.com/repos/groovy/groovy-­‐core/commits"   ! def  commits  =  new  JsonSlurper().parseText(url.toURL().text)   ! assert  commits[0].commit.author.name  ==  'Cedric  Champeau' GPath expression !58
  145. 145. GPath expressions • GPath expressions are like XPath 
 but for an object graph import  groovy.json.*   ! def  url  =  
    "https://api.github.com/repos/groovy/groovy-­‐core/commits"   ! def  commits  =  new  JsonSlurper().parseText(url.toURL().text)   ! assert  commits[0].commit.author.name  ==  'Cedric  Champeau' GPath expression !58 Add find / findAll into the mix
  146. 146. GPath expressions • GPath expressions are like XPath 
 No (un)marshalling! but for an object graph import  groovy.json.*   ! def  url  =  
    "https://api.github.com/repos/groovy/groovy-­‐core/commits"   ! def  commits  =  new  JsonSlurper().parseText(url.toURL().text)   ! assert  commits[0].commit.author.name  ==  'Cedric  Champeau' GPath expression !58 Add find / findAll into the mix
  147. 147. Power asserts def  (a,  b,  c)  =  [20,  30,  40]   ! assert  a  *  (b  -­‐  1)  /  10  ==  3  *  c  /  2  +  1 !59
  148. 148. Power asserts def  (a,  b,  c)  =  [20,  30,  40]   ! assert  a  *  (b  -­‐  1)  /  10  ==  3  *  c  /  2  +  1 Assertion  failed:     ! assert  a  *  (b  -­‐  1)  /  10  ==  3  *  c  /  2  +  1                |  |    |  |        |        |        |  |  |      |                |  580|  29      58      false|  |  60    61                20      30                              |  40                                                            120   !   at  script1.run(script1.groovy:3) !59
  149. 149. Power asserts def  (a,  b,  c)  =  [20,  30,  40]   ! assert  a  *  (b  -­‐  1)  /  10  ==  3  *  c  /  2  +  1 Assertion  failed:     ! assert  a  *  (b  -­‐  1)  /  10  ==  3  *  c  /  2  +  1                |  |    |  |        |        |        |  |  |      |                |  580|  29      58      false|  |  60    61                20      30                              |  40                                                            120   ! Invented by the Spock testing framework   at  script1.run(script1.groovy:3) !59
  150. 150. Null handling class  Order  {          LineItem  line   }   class  LineItem  {          int  quantity          Item  item   }   class  Item  {          String  name   }   ! def  o  =  new  Order(          line:  new  LineItem(                  quantity:  2,                  item:  null))   ! println  o.line.item.name !60
  151. 151. Null handling class  Order  {          LineItem  line   }   class  LineItem  {          int  quantity          Item  item   }   class  Item  {          String  name   }   ! With Java, you only get an NPE. No idea where it came from! def  o  =  new  Order(          line:  new  LineItem(                  quantity:  2,                  item:  null))   ! println  o.line.item.name !60
  152. 152. Null handling class  Order  {          LineItem  line   }   class  LineItem  {          int  quantity          Item  item   }   class  Item  {          String  name   }   ! With Java, you only get an NPE. No idea where it came from! def  o  =  new  Order(          line:  new  LineItem(                  quantity:  2,                  item:  null))   ! println  o.line.item.name !60 Groovy will say: Cannot get property ‘name’ on null object
  153. 153. Null handling class  Order  {          LineItem  line   }   class  LineItem  {          int  quantity          Item  item   }   class  Item  {          String  name   }   ! def  o  =  new  Order(          line:  new  LineItem(                  quantity:  2,                  item:  null))   ! println  o.line.item.name !60
  154. 154. Null handling class  Order  {          LineItem  line   }   class  LineItem  {          int  quantity          Item  item   }   class  Item  {          String  name   }   o?.line?.item?.name ! def  o  =  new  Order(          line:  new  LineItem(                  quantity:  2,                  item:  null))   ! println  o.line.item.name !60
  155. 155. Null handling class  Order  {          LineItem  line   }   class  LineItem  {          int  quantity          Item  item   }   class  Item  {          String  name   }   o?.line?.item?.name ! def  o  =  new  Order(          line:  new  LineItem(                  quantity:  2,                  item:  null))   ! println  o.line.item.name !60 Safe navigation: will just return null; No NPE
  156. 156. The Truth, the Groovy Truth!
  157. 157. The Truth, the Groovy Truth! And what if I could customize the truth?
  158. 158. The Groovy Truth assert  !(  null  )   assert  !(    ""    )   assert  !(    []    )   assert  !(      0    ) assert  new  Object()   assert  "string"   assert  [1,  2,  3]   assert  1234 !62
  159. 159. The Groovy Truth null, empty, 0-sized, zero are coerced to false assert  !(  null  )   assert  !(    ""    )   assert  !(    []    )   assert  !(      0    ) assert  new  Object()   assert  "string"   assert  [1,  2,  3]   assert  1234 !62
  160. 160. The Groovy Truth null, empty, 0-sized, zero are coerced to false assert  !(  null  )   assert  !(    ""    )   assert  !(    []    )   assert  !(      0    ) assert  new  Object()   assert  "string"   assert  [1,  2,  3]   assert  1234 !62 true otherwise
  161. 161. Customizing the truth! class  Account  {          String  name          boolean  disabled  =  false   !        boolean  asBoolean()  {  !disabled  }   }   ! assert    new  Account(name:  'current')   assert  !new  Account(name:  'old',  disabled:  true) !63
  162. 162. Customizing the truth! class  Account  {          String  name          boolean  disabled  =  false   !        boolean  asBoolean()  {  !disabled  }   }   ! assert    new  Account(name:  'current')   assert  !new  Account(name:  'old',  disabled:  true) while (account), if (account), etc… !63
  163. 163. ?:
  164. 164. ?: The Elvis operator!
  165. 165. Towards Elvis... !65
  166. 166. Towards Elvis... def  (x,  y)  =  ['MacBook  Pro',  'unknown'] !65
  167. 167. Towards Elvis... def  (x,  y)  =  ['MacBook  Pro',  'unknown'] !65
  168. 168. Towards Elvis... def  (x,  y)  =  ['MacBook  Pro',  'unknown'] if  (x  !=  null  &&  x.size()  >  0)  x  else  y !65
  169. 169. Towards Elvis... def  (x,  y)  =  ['MacBook  Pro',  'unknown'] if  (x  !=  null  &&  x.size()  >  0)  x  else  y if  (x  &&  x.size())  x  else  y !65
  170. 170. Towards Elvis... def  (x,  y)  =  ['MacBook  Pro',  'unknown'] if  (x  !=  null  &&  x.size()  >  0)  x  else  y if  (x  &&  x.size())  x  else  y if  (x)  x  else  y !65
  171. 171. Towards Elvis... def  (x,  y)  =  ['MacBook  Pro',  'unknown'] if  (x  !=  null  &&  x.size()  >  0)  x  else  y if  (x  &&  x.size())  x  else  y if  (x)  x  else  y x  ?  x  :  y !65
  172. 172. Towards Elvis... def  (x,  y)  =  ['MacBook  Pro',  'unknown'] if  (x  !=  null  &&  x.size()  >  0)  x  else  y if  (x  &&  x.size())  x  else  y if  (x)  x  else  y x  ?  x  :  y x  ?:  y !65
  173. 173. Towards Elvis... def  (x,  y)  =  ['MacBook  Pro',  'unknown'] if  (x  !=  null  &&  x.size()  >  0)  x  else  y if  (x  &&  x.size())  x  else  y if  (x)  x  else  y x  ?  x  :  y x  ?:  y Null, empty, zerosized... false, otherwise true! !65
  174. 174. Towards Elvis... def  (x,  y)  =  ['MacBook  Pro',  'unknown'] if  (x  !=  null  &&  x.size()  >  0)  x  else  y if  (x  &&  x.size())  x  else  y if  (x)  x  else  y x  ?  x  :  y x  ?:  y Good old ternary operator !65 Null, empty, zerosized... false, otherwise true!
  175. 175. Towards Elvis... def  (x,  y)  =  ['MacBook  Pro',  'unknown'] if  (x  !=  null  &&  x.size()  >  0)  x  else  y if  (x  &&  x.size())  x  else  y if  (x)  x  else  y x  ?  x  :  y x  ?:  y Elvis! !65 Good old ternary operator Null, empty, zerosized... false, otherwise true!
  176. 176. AST transformations • Abstract Syntax Tree – in memory representation of your program
 before being compiled into bytecode ! • AST transformation == process of transforming the AST of a program before it’s compiled ! • Macro-like compiler hook! !66
  177. 177. Lots of AST transformations... • Code generation – @ToString, @EqualsAndHashCode, @Canonical, @TupleConstructor, @InheritConstructors, @Category, @IndexedProperty, @Lazy, @Newify • Class design – @Delegate, @Immutable, @Memoized, 
 @Singleton, @Mixin • Logging – @Log, @Log4j, @Log4j2, @Slf4j !67
  178. 178. Lots of AST transformations... • Safer scripting – @ConditionalInterrupt, @ThreadInterrupt, @TimedInterupt • Compiler directives – @Field, @PackageScope, @AnnotationCollector, @DelegatesTo, @TypeChecked, @CompileStatic, @CompileDynamic • Swing patterns – @Bindable, @ListenerList, @Vetoable !68
  179. 179. Lots of AST transformations... • Dependencies handling – @Grab, @GrabConfig, @GrabExclude, @GrabResolver • Test assistance – @NotYetImplemented, @ASTTest !69
  180. 180. Immutability • Implement immutability 
 by the book ! – final class – tuple-style constructor – private final backing fields – defensive copying of collections – equals() and hashCode() methods – toString() method – ... !70
  181. 181. Immutability • Implement immutability 
 by the book ! – final class Can be error-prone to – tuple-style constructor write immutable – private final backing fields classes oneself! – defensive copying of collections – equals() and hashCode() methods – toString() method – ... !70
  182. 182. Immutability • A Person class with – a String name – an int age public final class Person {! private final String name;! private final int age;! ! ! ! ! ! ! }! !71 public Person(String name, int age) {! this.name = name;! this.age = age;! }! public String getName() {! return name;! }! public int getAge() {! return age;! }! public int hashCode() {! return age + 31 * name.hashCode();! }! public boolean equals(Object other) {! if (other == null) {! return false;! }! if (this == other) {! return true;! }! if (Person.class != other.getClass()) {! return false;! }! Person otherPerson = (Person)other;! if (!name.equals(otherPerson.getName()) {! return false;! }! if (age != otherPerson.getAge()) {! return false;! }! return true;! }! public String toString() {! return "Person(" + name + ", " + age + ")";! }!
  183. 183. Immutability • A Person class with – a String name – an int age public final class Person {! private final String name;! private final int age;! ! ! ! ! ! ! }! !71 Damn verbose Java! public Person(String name, int age) {! this.name = name;! this.age = age;! }! public String getName() {! return name;! }! public int getAge() {! return age;! }! public int hashCode() {! return age + 31 * name.hashCode();! }! public boolean equals(Object other) {! if (other == null) {! return false;! }! if (this == other) {! return true;! }! if (Person.class != other.getClass()) {! return false;! }! Person otherPerson = (Person)other;! if (!name.equals(otherPerson.getName()) {! return false;! }! if (age != otherPerson.getAge()) {! return false;! }! return true;! }! public String toString() {! return "Person(" + name + ", " + age + ")";! }!
  184. 184. Immutability • A Person class with – a String name – an int age Damn verbose Java! public final class Person {! private final String name;! private final int age;! ! ! ! ! ! public Person(String name, int age) {! this.name = name;! this.age = age;! }! public String getName() {! return name;! }! public int getAge() {! return age;! }! public int hashCode() {! return age + 31 * name.hashCode();! }! public boolean equals(Object other) {! if (other == null) {! return false;! }! if (this == other) {! return true;! }! if (Person.class != other.getClass()) {! return false;! }! Person otherPerson = (Person)other;! if (!name.equals(otherPerson.getName()) {! return false;! }! if (age != otherPerson.getAge()) {! return false;! }! return true;! }! Although it’s also a valid Groovy program! ! }! !71 public String toString() {! return "Person(" + name + ", " + age + ")";! }!
  185. 185. @Immutable import  groovy.transform.*   ! @Immutable   class  Person  {          String  name          int  age   } !72
  186. 186. Memoization • Cache the result of previous invocations of closures or methods with the same set 
 of argument values import  groovy.transform.*   ! @Memoized   long  fib(long  n)  {          if  (n  ==  0)  0          else  if  (n  ==  1)  1          else  fib(n  -­‐  1)  +  fib(n  -­‐  2)   }   ! println  fib(40) !73
  187. 187. Memoization • Cache the result of previous invocations of closures or methods with the same set 
 of argument values import  groovy.transform.*   Best applied to side-effect free functions ! @Memoized   long  fib(long  n)  {          if  (n  ==  0)  0          else  if  (n  ==  1)  1          else  fib(n  -­‐  1)  +  fib(n  -­‐  2)   }   ! println  fib(40) !73
  188. 188. Groovy allows you to be lazy
  189. 189. Groovy allows you to be lazy The compiler will do the job for you
  190. 190. Groovy allows you to be lazy More concise, more readable code The compiler will do the job for you
  191. 191. Groovy allows you to be lazy More concise, more readable code The compiler will do the job for you Less stuff to maintain and worry about
  192. 192. @TypeChecked & @CompileStatic • Static type checking with @TypeChecked, throws compilation errors on... – typos in method and variable names – incompatible return types – wrong type assignments ! • Supports fine-grained type inference – « Least Upper Bound » – « Flow typing » !75
  193. 193. @TypeChecked & @CompileStatic • Static type checking with @TypeChecked, throws compilation errors on... – typos in method and variable names – incompatible return types You can even extend the – wrong type assignments static type checker! ! • Supports fine-grained type inference – « Least Upper Bound » – « Flow typing » !75
  194. 194. @TypeChecked & @CompileStatic • Static type checking with @TypeChecked, throws compilation errors on... – typos in method and variable names – incompatible return types You can even extend the – wrong type assignments ! static type checker! • Supports fine-grained type inference – « Least Upper Bound » – « Flow typing » Type check DSLs or dynamic features! !75
  195. 195. @TypeChecked & @CompileStatic ! • What is type checked can also be compiled statically with @CompileStatic ! – generate the same bytecode as javac ! – same performance as Java !76
  196. 196. Pi (π) Fibonacci quadrature Java 191 ms 97 ms 3.6 s 2.x Static
 compilation 197 ms 101 ms 4.3 s 1.8 !77 Binary
 trees Primitive optimizations 360 ms 111 ms 23.7 s 1.7 Static compilation performance No prim.
 optimizations 2590 ms 3220 ms 50.0 s
  197. 197. 3 t r a P Superb community!
  198. 198. A blossoming Ecosystem
  199. 199. M V G
  200. 200. GVM
  201. 201. GVM GROOVY ENVIRONMENT MANAGER
  202. 202. GVM: Groovy enVironment Manager • The new kid on the block – http://gvmtool.net/ — @gvmtool ! • Manage parallel versions 
 of the various ecosystem projects ! • Supports... – Groovy, Grails, Griffon, Gradle,Vert.x, Spring Boot ! • On Linux, MacOS, Cygwin, Solaris, FreeBSD !86
  203. 203. I’m Spock...
  204. 204. I’m Spock... ...the Spock testing framework
  205. 205. I’m Spock... ...the Spock testing framework
  206. 206. Spock example @Grab('org.spockframework:spock-­‐core:0.7-­‐groovy-­‐2.0')   import  spock.lang.*   ! class  MathSpec  extends  Specification  {          def  "maximum  of  two  numbers"()  {              expect:                  Math.max(a,  b)  ==  c   !            where:                  a  |  b  ||  c                  1  |  3  ||  3                  7  |  4  ||  4                  0  |  0  ||  0          }   } !88
  207. 207. Spock example @Grab a dependency @Grab('org.spockframework:spock-­‐core:0.7-­‐groovy-­‐2.0')   import  spock.lang.*   ! class  MathSpec  extends  Specification  {          def  "maximum  of  two  numbers"()  {              expect:                  Math.max(a,  b)  ==  c   !            where:                  a  |  b  ||  c                  1  |  3  ||  3                  7  |  4  ||  4                  0  |  0  ||  0          }   } !88
  208. 208. Spock example @Grab a dependency @Grab('org.spockframework:spock-­‐core:0.7-­‐groovy-­‐2.0')   import  spock.lang.*   ! class  MathSpec  extends  Specification  {          def  "maximum  of  two  numbers"()  {              expect:                  Math.max(a,  b)  ==  c   !            where:                  a  |  b  ||  c                  1  |  3  ||  3                  7  |  4  ||  4                  0  |  0  ||  0          }   } !88 Meaningful test method names
  209. 209. Spock example @Grab a dependency @Grab('org.spockframework:spock-­‐core:0.7-­‐groovy-­‐2.0')   import  spock.lang.*   ! Meaningful test method names class  MathSpec  extends  Specification  {          def  "maximum  of  two  numbers"()  {              expect:                  Math.max(a,  b)  ==  c   Clever use of labels !            where:                  a  |  b  ||  c                  1  |  3  ||  3                  7  |  4  ||  4                  0  |  0  ||  0          }   } !88 for BDD style
  210. 210. Spock example @Grab a dependency @Grab('org.spockframework:spock-­‐core:0.7-­‐groovy-­‐2.0')   import  spock.lang.*   ! Meaningful test method names class  MathSpec  extends  Specification  {          def  "maximum  of  two  numbers"()  {              expect:                  Math.max(a,  b)  ==  c   Clever use of labels !            where:                  a  |  b  ||  c                  1  |  3  ||  3                  7  |  4  ||  4                  0  |  0  ||  0          }   } !88 for BDD style Expression to be asserted
  211. 211. Spock example @Grab a dependency @Grab('org.spockframework:spock-­‐core:0.7-­‐groovy-­‐2.0')   import  spock.lang.*   Meaningful test method names ! class  MathSpec  extends  Specification  {          def  "maximum  of  two  numbers"()  {              expect:                  Math.max(a,  b)  ==  c   Clever use of labels for BDD style !            where:                  a  |  b  ||  c                  1  |  3  ||  3                  7  |  4  ||  4                  0  |  0  ||  0          }   } !88 Cute datadriven tests! Expression to be asserted
  212. 212. @GrabResolver("https://oss.jfrog.org/artifactory/repo")   @Grab("org.ratpack-­‐framework:ratpack-­‐groovy:0.9.0-­‐SNAPSHOT")   import  static  org.ratpackframework.groovy.RatpackScript.ratpack   import  static  org.ratpackframework.groovy.Template.groovyTemplate   ! ratpack  {          handlers  {                  get  {                          response.send  "Welcome!"                  }   !                get("date")  {                          render  groovyTemplate("date.html")                  }   !                assets  "public"          }   }
  213. 213. @GrabResolver("https://oss.jfrog.org/artifactory/repo")   @Grab("org.ratpack-­‐framework:ratpack-­‐groovy:0.9.0-­‐SNAPSHOT")   import  static  org.ratpackframework.groovy.RatpackScript.ratpack   import  static  org.ratpackframework.groovy.Template.groovyTemplate   ! ratpack  {          handlers  {                  get  {                          response.send  "Welcome!"                  }   Lightweight Netty-based web app toolkit !                get("date")  {                          render  groovyTemplate("date.html")                  }   !                assets  "public"          }   }
  214. 214. Geb • Browser automation solution ! • WebDriver + jQuery selectors + Groovy ! • Handy for – scripting, scraping, automation... – functional / web / acceptance testing • when integrated with JUnit, TestNG or Spock !90
  215. 215. Geb — Example import  geb.Browser   ! Browser.drive  {          go  "http://myapp.com/login"   !        assert  $("h1").text()  ==  "Please  Login"   !        $("form.login").with  {                  username  =  "admin"                  password  =  "password"                  login().click()          }   !        assert  $("h1").text()  ==  
                "Admin  Section"   }   !91
  216. 216. Geb — Example Drive the browser to this site import  geb.Browser   ! Browser.drive  {          go  "http://myapp.com/login"   !        assert  $("h1").text()  ==  "Please  Login"   !        $("form.login").with  {                  username  =  "admin"                  password  =  "password"                  login().click()          }   !        assert  $("h1").text()  ==  
                "Admin  Section"   }   !91
  217. 217. Geb — Example Drive the browser to this site import  geb.Browser   ! Browser.drive  {          go  "http://myapp.com/login"   Check the content of the title !        assert  $("h1").text()  ==  "Please  Login"   !        $("form.login").with  {                  username  =  "admin"                  password  =  "password"                  login().click()          }   !        assert  $("h1").text()  ==  
                "Admin  Section"   }   !91
  218. 218. Geb — Example Drive the browser to this site import  geb.Browser   ! Browser.drive  {          go  "http://myapp.com/login"   Check the content of the title !        assert  $("h1").text()  ==  "Please  Login"   !        $("form.login").with  {                  username  =  "admin"                  password  =  "password"                  login().click()          }   !        assert  $("h1").text()  ==  
                "Admin  Section"   }   !91 Find & fill in the form
  219. 219. Geb — Example Drive the browser to this site import  geb.Browser   ! Browser.drive  {          go  "http://myapp.com/login"   Check the content of the title !        assert  $("h1").text()  ==  "Please  Login"   !        $("form.login").with  {                  username  =  "admin"                  password  =  "password"                  login().click()          }   !        assert  $("h1").text()  ==  
                "Admin  Section"   }   !91 Find & fill in the form Submit the form
  220. 220. Geb — Example Drive the browser to this site import  geb.Browser   ! Browser.drive  {          go  "http://myapp.com/login"   Check the content of the title !        assert  $("h1").text()  ==  "Please  Login"   !        $("form.login").with  {                  username  =  "admin"                  password  =  "password"                  login().click()          }   !        assert  $("h1").text()  ==  
                "Admin  Section"   }   Find & fill in the form Submit the form In the admin section, yeah! !91
  221. 221. Geb — With page objects and Spock import  geb.spock.GebSpec   ! class  GoogleWikipediaSpec  extends  GebSpec  {   !        def  "first  result  for  wikipedia  search  should  be  wikipedia"()  {                  given:                  to  GoogleHomePage   !                expect:                  at  GoogleHomePage   !                when:                  search.field.value("wikipedia")   !                then:                  waitFor  {  at  GoogleResultsPage  }   !                and:                  firstResultLink.text()  ==  "Wikipedia"   !                when:                  firstResultLink.click()   !                then:                  waitFor  {  at  WikipediaPage  }          }   } !92
  222. 222. Geb — With page objects and Spock import  geb.spock.GebSpec   ! With page objects        def  "first  result  for  wikipedia  search  should  be  wikipedia"()  {   class  GoogleWikipediaSpec  extends  GebSpec  {   !                given:                  to  GoogleHomePage   !                expect:                  at  GoogleHomePage   !                when:                  search.field.value("wikipedia")   !                then:                  waitFor  {  at  GoogleResultsPage  }   !                and:                  firstResultLink.text()  ==  "Wikipedia"   !                when:                  firstResultLink.click()   !                then:                  waitFor  {  at  WikipediaPage  }          }   } !92
  223. 223. Geb — With page objects and Spock import  geb.spock.GebSpec   ! With page objects        def  "first  result  for  wikipedia  search  should  be  wikipedia"()  {   class  GoogleWikipediaSpec  extends  GebSpec  {   !                given:                  to  GoogleHomePage   !                expect:                  at  GoogleHomePage   BDD style: given/when/then !                when:                  search.field.value("wikipedia")   !                then:                  waitFor  {  at  GoogleResultsPage  }   !                and:                  firstResultLink.text()  ==  "Wikipedia"   !                when:                  firstResultLink.click()   !                then:                  waitFor  {  at  WikipediaPage  }          }   } !92
  224. 224. Geb — With page objects and Spock import  geb.spock.GebSpec   ! With page objects        def  "first  result  for  wikipedia  search  should  be  wikipedia"()  {   class  GoogleWikipediaSpec  extends  GebSpec  {   !                given:                  to  GoogleHomePage   !                expect:                  at  GoogleHomePage   BDD style: given/when/then !                when:                  search.field.value("wikipedia")   ! Wait for slow loading pages                then:                  waitFor  {  at  GoogleResultsPage  }   !                and:                  firstResultLink.text()  ==  "Wikipedia"   !                when:                  firstResultLink.click()   !                then:                  waitFor  {  at  WikipediaPage  }          }   } !92
  225. 225. 4 t r a P Summary
  226. 226. Java’s best friend • Java derived syntax –Flat learning curve –Easy to learn • But goes beyond Java –Concise, expressive, readable –Fit for Domain-Specific Languages • Seamless & transparent Java integration –Mix & match Groovy & Java classes (joint compil.) –No language barrier to cross !94
  227. 227. Groovy’s nature ! • Object oriented dynamic language... ! • But... – as type safe as you want it — static type checking – as fast as you need it — static compilation – as functional as you make it — closures... !95
  228. 228. Groovy use cases • Scripting tasks, build automation • Extension points for customizing/configuring apps • Business languages & Domain-Specific Languages • Full blown apps – for desktop with Griffon – for the web with Grails, Ratpack, Gaelyk – for web reactive programming with Reactor !96
  229. 229. 5 t r a P Thanks! Q&A

×