Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Groovy with Style -- Groovy Grails eXchange 2014

Groovy with Style -- Groovy Grails eXchange 2014

What should "idiomatic" Groovy code look like? What are the good practices in terms of syntax style, typing preferences? What are the nice shortcuts to be aware of to be more productive? Guillaume Laforge will answer those questions along the way!

Guillaume Laforge

December 12, 2014
Tweet

More Decks by Guillaume Laforge

Other Decks in Programming

Transcript

  1. <=>

  2. @glaforge No semicolons! 14 class HelloLondon {
 private String message

    = 'Hello London';
 String greet(String name) {
 "$message, $name speaking";
 } 
 }
 
 print new HelloLondon().greet('Guillaume');
  3. @glaforge No semicolons! 14 class HelloLondon {
 private String message

    = 'Hello London';
 String greet(String name) {
 "$message, $name speaking";
 } 
 }
 
 print new HelloLondon().greet('Guillaume');
  4. @glaforge No semicolons! 15 class HelloLondon {
 private String message

    = 'Hello London'
 String greet(String name) {
 "$message, $name speaking"
 } 
 }
 
 print new HelloLondon().greet('Guillaume')
  5. @glaforge Public by default 17 public class HelloLondon {
 private

    String message = 'Hello London'
 private String name
 
 public HelloLondon(String name) {
 this.name = name
 }
 public String greet() {
 return "$message, $name speaking"
 } 
 }
  6. @glaforge Public by default 18 class HelloLondon {
 private String

    message = 'Hello London'
 private String name
 
 HelloLondon(String name) {
 this.name = name
 }
 String greet() {
 return "$message, $name speaking"
 } 
 } Methods and classes are public by default
  7. @glaforge Fields and static fields require visibility 19 class HelloLondon

    {
 static String message = 'Hello London'
 String name
 
 HelloLondon(String name) {
 this.name = name
 }
 String greet() {
 return "$message, $name speaking"
 } 
 }
  8. @glaforge Fields and static fields require visibility 19 class HelloLondon

    {
 static String message = 'Hello London'
 String name
 
 HelloLondon(String name) {
 this.name = name
 }
 String greet() {
 return "$message, $name speaking"
 } 
 } You created both an instance property and a static property!
  9. @glaforge Get rid of the useless ‘def’ 21 class HelloLondon

    {
 private String message = 'Hello London'
 private def String name
 def HelloLondon(def String name) {
 this.name = name
 }
 public def String greet() {
 def String result = "$message, $name speaking"
 return result
 } 
 }

  10. @glaforge Get rid of the useless ‘def’ 21 class HelloLondon

    {
 private String message = 'Hello London'
 private def String name
 def HelloLondon(def String name) {
 this.name = name
 }
 public def String greet() {
 def String result = "$message, $name speaking"
 return result
 } 
 }

  11. @glaforge Get rid of the useless ‘def’ 22 class HelloLondon

    {
 private String message = 'Hello London'
 private String name
 HelloLondon(String name) {
 this.name = name
 }
 String greet() {
 String result = "$message, $name speaking"
 return result
 } 
 }

  12. @glaforge Get rid of the useless ‘def’ 23 class HelloLondon

    {
 private String message = 'Hello London'
 private String name
 HelloLondon(String name) {
 this.name = name
 }
 String greet() {
 def result = "$message, $name speaking"
 return result
 } 
 }
 But it’s fine to use ‘def’ for local variables
  13. @glaforge Return is optional too 25 String greet() { 


    return "$message, $name speaking"
 }
  14. @glaforge Return is optional too 25 String greet() { 


    return "$message, $name speaking"
 }
  15. @glaforge Return is optional too 26 String greet() { 


    "$message, $name speaking"
 } But return is fine for early returns, or for clarity of intent
  16. @glaforge Optional return for if / else 27 int color(String

    name) {
 if (name == 'red')
 1
 else if (name == 'green')
 2
 else
 -1
 }
  17. @glaforge Optional return for switch / case 28 int color(String

    name) {
 switch (name) {
 case 'red': 1; break
 case 'green': 2; break
 default: -1
 }
 }
  18. @glaforge Optional return for switch / case 28 int color(String

    name) {
 switch (name) {
 case 'red': 1; break
 case 'green': 2; break
 default: -1
 }
 } Yeah, yeah, ugly semicolon and break…
  19. @glaforge Optional return for try / catch 29 boolean isInt(String

    s) {
 try {
 Integer.parseInt(s)
 true
 } catch (NumberFormatException nfe) {
 false
 } finally {
 println 'done'
 }
 }
 
 assert isInt('123') && assert !isInt('abc')
  20. @glaforge Optional return for try / catch 29 boolean isInt(String

    s) {
 try {
 Integer.parseInt(s)
 true
 } catch (NumberFormatException nfe) {
 false
 } finally {
 println 'done'
 }
 }
 
 assert isInt('123') && assert !isInt('abc') ‘finally’ blocks don’t return a value
  21. @glaforge Command chains and named arguments 34 check that: wine

    tastes good Parsed as: check(that: wine).tastes(good)
  22. @glaforge Trailing closure argument gets out 35 void unless(boolean b,

    Closure c) {
 if (!b) c()
 }
 
 def (speed, limit) = [80, 90]
 
 unless(speed > limit) {
 println "speed up a notch"
 }
  23. @glaforge Trailing closure argument gets out 35 void unless(boolean b,

    Closure c) {
 if (!b) c()
 }
 
 def (speed, limit) = [80, 90]
 
 unless(speed > limit) {
 println "speed up a notch"
 } Parsed as: unless(speed > limit, {…})
  24. @glaforge No need for dumb getters and setters 40 class

    Person {
 private String name
 private int age
 
 void setName(String name) {
 this.name = name
 }
 String getName() {
 this.name
 }
 void setAge(int age) {
 this.age = age
 }
 int getAge() {
 this.age
 }
 }
  25. @glaforge No need for dumb getters and setters 41 class

    Person {
 String name
 int age
 }
  26. @glaforge No need for dumb getters and setters 41 class

    Person {
 String name
 int age
 } Icing on the cake: add @Immutable or @Canonical
  27. @glaforge Use the property notation 42 class Person {
 String

    name
 int age
 }
 
 def p = new Person(name: 'Guillaume', 
 age: 36)
 assert p.name == 'Guillaume'
 
 p.age = 37
 assert p.age == 37
  28. @glaforge Use the named-argument constructor 44 
 
 
 


    
 def p = new Person(name: 'Guillaume', 
 age: 37)
 
 
 

  29. @glaforge The @Builder transformation 45 import groovy.transform.builder.*
 
 @Builder
 class

    Person {
 String name
 int age
 }
 
 Person.builder() .name('Guillaume') .age(37) .build()
  30. @glaforge The @Builder transformation 45 import groovy.transform.builder.*
 
 @Builder
 class

    Person {
 String name
 int age
 }
 
 Person.builder() .name('Guillaume') .age(37) .build() Different strategies available: chained setters, external builder class, type-safe initializer
  31. @glaforge Native syntax for data structures 52 def list =

    [1, 2, 3]
 def range = 'a'..'b'
 def map = [a: 1, b: 2, c: 3]
 def regex = ~/.*foo.*/
  32. @glaforge Switch / case on steroids 56 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"
 }
  33. @glaforge Import aliasing 58 import java.util.List as juList
 import java.awt.List

    as aList
 
 import javax.swing.WindowConstants as WC
  34. @glaforge Import aliasing with static imports too 59 import static

    java.lang.Integer.parseInt as atoi
 
 assert atoi('123') == 123
  35. @glaforge Groovy Truth 61 assert !( null )
 assert !(

    "" )
 assert !( [] )
 assert !( 0 ) assert new Object()
 assert "string"
 assert [1, 2, 3]
 assert 1234
  36. @glaforge Groovy Truth 61 assert !( null )
 assert !(

    "" )
 assert !( [] )
 assert !( 0 ) assert new Object()
 assert "string"
 assert [1, 2, 3]
 assert 1234 False
  37. @glaforge Groovy Truth 61 assert !( null )
 assert !(

    "" )
 assert !( [] )
 assert !( 0 ) assert new Object()
 assert "string"
 assert [1, 2, 3]
 assert 1234 False True
  38. @glaforge Customize the truth with asBoolean() 62 class Account {


    String name
 boolean disabled = false
 
 boolean asBoolean() { !disabled }
 }
 
 assert new Account(name: 'current')
 assert !new Account(name: 'old', disabled: true)
  39. @glaforge Safe navigation 64 // Java if (order != null)

    {
 if (order.getCustomer() != null) {
 if (order.getCustomer().getAddress() != null) {
 System.out.println(order.getCustomer().getAddress());
 }
 }
 }
 // Groovy println order?.customer?.address
  40. @glaforge Safe navigation 64 // Java if (order != null)

    {
 if (order.getCustomer() != null) {
 if (order.getCustomer().getAddress() != null) {
 System.out.println(order.getCustomer().getAddress());
 }
 }
 }
 // Groovy println order?.customer?.address Also give more meaningful NPEs!
  41. @glaforge Power Assert, coming from Spock 66 def (a, b,

    c, d) = [10, 20, 30, 610]
 assert a + b * c == d - 40
  42. @glaforge Power Assert, coming from Spock 66 def (a, b,

    c, d) = [10, 20, 30, 610]
 assert a + b * c == d - 40 Assertion failed:
 
 assert a + b * c == d - 40
 | | | | | | | |
 10| 20| 30| | 570
 610 600 | 610
 false
  43. @glaforge Calling Elvis for default values 68 def result =

    name != null ? name : "Unknown"
 
 def result = name ?: "Unknown"
  44. @glaforge Calling Elvis for default values 68 def result =

    name != null ? name : "Unknown"
 
 def result = name ?: "Unknown" Groovy Truth 
 at play here
  45. @glaforge Catch ‘any’ exception 70 try {
 something()
 } catch

    (any) {
 // ...
 } Someone suggested me: catch(emAll)
  46. @glaforge Of the importance of contracts • Use explicit typing

    for – method signatures – properties & fields • Nice for – Java interoperability – documenting your APIs – IDE auto-completion and refactoring 72
  47. @glaforge Picture credits Style: http://1hdwallpapers.com/wallpapers/my_style.jpg Eurostar: http://www.whatsupwhatson.com/wp-content/uploads/2014/04/Eurostar_3012_Waterloo.jpg Puzzle: http://joyinthecause.org/wp-content/uploads/2014/03/missing-puzzle-piece_00449088.jpg Gems:

    http://www.freewhd.com/blue-gems-wallpaper/ Wise: http://fc00.deviantart.net/fs70/i/2012/044/f/6/three_wise_men_color_by_csoro-d4pmlv2.jpg Ready: http://nameshapers.com/wp-content/uploads/2013/12/Social-Media-Coaching-for-CEOs-Getting-started.jpg Semicolon: http://www.freeimageslive.co.uk/image/view/3944/_original Till death to us part: https://mypencillines.files.wordpress.com/2013/04/img_0171.jpg Public: http://www.levelupliving.com/wp-content/uploads/2012/05/Fear-of-Public-Speaking-image.jpg Contract: https://www.icts.uiowa.edu/sites/default/files/contract.jpg Exception: http://www.quotescover.com/wp-content/uploads/Its-the-exception-that-proves__quotes-by-French-Proverb-77.png Banana: http://texturetaddka.com/wp-content/uploads/2011/10/DSC2008.jpg Elvis: http://natedsanders.com/blog/wp-content/uploads/2013/10/Elvis-Presley-Wallpaper-1280-x-960.jpeg Assert: http://www.itshouldjustworktm.com/wp-content/uploads/2012/03/assert-sign-photoshopped.jpg Compass: https://lh4.ggpht.com/WtY8QL0re9kMssF6q4RopDHC6QEpou7ybQo7qNRjn1X8Iu5uKUZv7huHA_PxPB8iGfs=h900 Safety vest: http://media.nautisports.com/catalog/product/image/gilet-de-sauvetage-gonflable-hydrostatique-hammar-pilot-275-n-avec-harnais-pro-rouge-nav_17452_web_1_1.jpg Class: http://upload.wikimedia.org/wikipedia/en/5/50/A_Class_in_Progress.jpg Aliasing: http://i.imgur.com/JrFly.png Light switch: http://www.hapihour.org/wp-content/uploads/2014/04/lighting-design-wall-light-winsome-designer-light-switches-australia-designer-light-switchesdesigner-light-switches-and-platesdesigner-light- switches-coversdesigner-light-switches-graydesign.jpg Steel structure: http://www.jingtabuilding.com/UploadFiles/sl6.jpg Quotes: http://g.fastcompany.net/multisite_files/fastcompany/poster/2013/09/3018353-poster-1280-quotess3.jpg Setter: http://upload.wikimedia.org/wikipedia/commons/b/b4/Rzesz%C3%B3w_Irish_Red_and_White_Setter_1pl.jpg Argument: http://www.familytreecounseling.com/kathysblog/wp-content/uploads/2014/01/arguing_couple.jpg Equal cupcake: http://static.communitytable.com/wp-content/uploads/2013/04/marriage-equality-cupcakes-ftr.jpg Obama truth: http://upload.wikimedia.org/wikipedia/commons/7/7c/Obama_swearing_in.JPG Quiz: http://a622.phobos.apple.com/us/r30/Purple/v4/df/a9/6e/dfa96e6c-ea9b-ee4c-4d6c-f9edf2775e1d/mzl.exfkkuro.png return: http://legacyofshadow.com/wp-content/uploads/2012/04/no-return-sign.jpg spock: http://trekcore.com/gallery/albums/spock09/spock_pb04.jpg London: http://www.100percentoptical.com/images/2014/10/london.jpg Tie fighter: http://img3.wikia.nocookie.net/__cb20090419001710/uncyclopedia/images/f/fe/Darthvader_tie_fighter.jpg 75