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.

Effective Java with Groovy

Slides from my GR8Conf EU 2019 presentation - 'Effective Java with Groovy'

  • Login to see the comments

Effective Java with Groovy

  1. 1. Effective Java With Groovy Naresha K @naresha_k https://blog.nareshak.com/
  2. 2. About me Developer, Coach, Consultant Founder & Organiser Bangalore Groovy User Group
  3. 3. Data Information Knowledge Wisdom
  4. 4. https://www.flickr.com/photos/hchalkley/30724738
  5. 5. initial idea was to make a little dynamic language which compiles directly to Java classes and provides all the nice (alleged) productivity benefits - James Strachan http://radio-weblogs.com/0112098/2003/08/29.html
  6. 6. http://commons.wikimedia.org/wiki/File:You-Are-Here-Earth.png
  7. 7. http://upload.wikimedia.org/wikipedia/commons/4/41/Just_ask_%26_You_are_here_-.jpg
  8. 8. Know your guides Context/ the Problem What does “Effective Java” say? The traps Groovier Solution Lessons Learned Tool Wisdom
  9. 9. class Product { String sku String description BigDecimal price }
  10. 10. Product book1 = new Product( sku: 'P101', description: 'Effective Java’, price: 40.0) Product book2 = new Product( sku: 'P101', description: 'Effective Java’, price: 40.0) println book1 == book2 false
  11. 11. Product book1 = new Product( sku: 'P101', description: 'Effective Java’, price: 40.0) Product book2 = new Product( sku: 'P101', description: 'Effective Java’, price: 40.0) def stock = [:] stock[book1] = 100 println stock[book2] null
  12. 12. #8: Obey the general contract when overriding equals #9: Always override hashCode when you override equals
  13. 13. class Product { String sku String description BigDecimal price } class Product { String sku String description BigDecimal price LocalDate dateOfManufacture } class Product { String sku String description BigDecimal price LocalDate dateOfManufacture LocalDate dateOfExpiry }
  14. 14. @EqualsAndHashCode class Product { String sku String description BigDecimal price }
  15. 15. Product book1 = new Product( sku: 'P101', description: 'Effective Java’, price: 40.0) Product book2 = new Product( sku: 'P101', description: 'Effective Java’, price: 40.0) println book1 == book2 true
  16. 16. Product book1 = new Product( sku: 'P101', description: 'Effective Java’, price: 40.0) Product book2 = new Product( sku: 'P101', description: 'Effective Java’, price: 40.0) def stock = [:] stock[book1] = 100 println stock[book2] 100
  17. 17. @EqualsAndHashCode(includes = 'id') class Product { Long id String sku String description BigDecimal price static constraints = { sku unique: true } }
  18. 18. @EqualsAndHashCode(includes = 'id') class Product { Long id String sku String description BigDecimal price static constraints = { sku unique: true } }
  19. 19. @EqualsAndHashCode(includes = ‘sku') class Product { Long id String sku String description BigDecimal price static constraints = { sku unique: true } }
  20. 20. AST Transformation Single point of representation of any knowledge
  21. 21. #46: Prefer for-each loops to traditional for loops
  22. 22. def numbers = [10, 20, 30, 40] def sum = 0 for(int number in numbers){ sum += number } println "Sum: " + sum
  23. 23. numbers.each { number -> println number } println numbers.collect { number -> number * 2 } println numbers.inject(0, { result, number -> result + number } ) 10 20 30 40 [20, 40, 60, 80] 100
  24. 24. Closures Favor Internal iterators to external iterators Minimise the moving parts
  25. 25. float price = 0.1f; float total = 0; for(int i=0; i<10; i++){ total += price; } System.out.println("Total: " + total); Total: 1.0000001
  26. 26. double price = 0.1; double total = 0; for(int i=0; i<10; i++){ total += price; } System.out.println("Total: " + total); Total: 0.9999999999999999
  27. 27. #48: Avoid float and double if exact answers are required
  28. 28. BigDecimal price = new BigDecimal(0.1); BigDecimal total = new BigDecimal(0); for(int i=0; i<10; i++){ total = total.add(price); } System.out.println("Total: " + total); Total: 1.0000000000000000555111512312578270211815834045410156250
  29. 29. BigDecimal price = new BigDecimal(0.1); BigDecimal total = new BigDecimal(0); for(int i=0; i<10; i++){ total = total.add(price); } System.out.println("Total: " + total); Total: 1.0000000000000000555111512312578270211815834045410156250
  30. 30. def price = 0.1 def total = 0 for(int i=0; i<10; i++){ total += price } println "Total: " + total Total: 1.0
  31. 31. def price = 0.1 def total = 0 for(int i=0; i<10; i++){ total += price } println "Total: " + total println price.class println total.class Total: 1.0 class java.math.BigDecimal class java.math.BigDecimal
  32. 32. Select appropriate default Principle of Least Astonishment
  33. 33. Bringing in Order class Person { String name int age } def geeks = [ new Person(name: 'Raj', age: 30), new Person(name: 'Leonard', age: 35), new Person(name: 'Sheldon', age: 32), new Person(name: 'Sheldon', age: 25), new Person(name: 'Penny', age: 35) ]
  34. 34. #12: Consider implementing Comparable
  35. 35. println 10 <=> 20 println 20 <=> 10 println 10 <=> 10 -1 1 0
  36. 36. @ToString class Person implements Comparable<Person>{ String name int age int compareTo(Person other){ name <=> other.name } }
  37. 37. geeks.sort(false) Original: [Person(Raj, 30), Person(Leonard, 35), Person(Sheldon, 32), Person(Sheldon, 25), Person(Penny, 35)] Sorted: [Person(Leonard, 35), Person(Penny, 35), Person(Raj, 30), Person(Sheldon, 32), Person(Sheldon, 25)]
  38. 38. geeks.sort(false) { a, b -> a.age <=> b.age} Original: [Person(Raj, 30), Person(Leonard, 35), Person(Sheldon, 32), Person(Sheldon, 25), Person(Penny, 35)] Sorted: [Person(Sheldon, 25), Person(Raj, 30), Person(Sheldon, 32), Person(Leonard, 35), Person(Penny, 35)] Sort By Age
  39. 39. Sort By Age, Name int compareTo(Person other) { if (this.is(other)) { return 0 } java.lang.Integer value = 0 value = this.age <=> other.age if ( value != 0) { return value } value = this.name <=> other.name if ( value != 0) { return value } return 0 }
  40. 40. geeks.sort(false, { a, b -> [{it.age}, {it.name}].findResult { c -> c(a) <=> c(b) ?: null } }) Original: [Person(Raj, 30), Person(Leonard, 35), Person(Sheldon, 32), Person(Sheldon, 25), Person(Penny, 35)] Sorted: [Person(Sheldon, 25), Person(Raj, 30), Person(Sheldon, 32), Person(Leonard, 35), Person(Penny, 35)] Sort By Age, Name
  41. 41. @ToString @Sortable(includes = "age, name") class Person { String name int age } Original: [Person(Raj, 30), Person(Leonard, 35), Person(Sheldon, 32), Person(Sheldon, 25), Person(Penny, 35)] Sort By Age, Name Sorted: [Person(Sheldon, 25), Person(Raj, 30), Person(Sheldon, 32), Person(Leonard, 35), Person(Penny, 35)]
  42. 42. Syntactic Sugar: Spaceship Operator Simplify common tasks AST Transformation
  43. 43. Million Dollor Effort
  44. 44. Million Dollor Effort null check
  45. 45. List<Speaker> getSpeakers(String conference){ null } def gr8Speakers = getSpeakers('GR8Conf EU 2019') if(gr8Speakers != null){ // ... }
  46. 46. #43: Return empty arrays or collections, not nulls
  47. 47. println getSpeakers('GR8Conf EU 2019') .collect { it.firstName } println getSpeakers('GR8Conf EU 2019') .findAll { it.firstName.length() > 5 }
  48. 48. println getSpeakers('GR8Conf EU 2019') .collect { it.firstName } // [] println getSpeakers('GR8Conf EU 2019') .findAll { it.firstName.length() > 5 } // []
  49. 49. NullObject Pattern No boilerplate code - $$$ Life is too short for null checks!
  50. 50. #3: Enforce the singleton property with a private constructor or an enum type
  51. 51. class Manager { private static final Manager manager = new Manager() private Manager() { super() } static Manager getInstance() { manager } } def m1 = Manager.getInstance() def m2 = Manager.getInstance() println m1 == m2 true
  52. 52. class Manager { private static final Manager manager = new Manager() private Manager() { super() } static Manager getInstance() { manager } } def m1 = Manager.getInstance() def m2 = Manager.getInstance() println m1 == m2 true def m3 = new Manager() println m3 Manager@b968a76
  53. 53. @Singleton class Manager {} def m1 = Manager.getInstance() def m2 = Manager.getInstance() println m1 == m2 def m3 = new Manager() println m3 true Caught: java.lang.RuntimeException: Can't instantiate singleton Manager. Use Manager.instance
  54. 54. private static Manager manager static Manager getInstance() { if(!manager){ manager = new Manager() } manager }
  55. 55. private static Manager manager static synchronized Manager getInstance() { if(!manager){ manager = new Manager() } manager }
  56. 56. private static volatile Manager manager static synchronized Manager getInstance() { if(!manager){ manager = new Manager() } manager }
  57. 57. @Singleton(lazy=true) class Manager {} public static Manager getInstance() { if (instance != null) { return instance } else { synchronized (Manager) { if (instance != null) { return instance } else { return instance = new Manager() } } } }
  58. 58. AST Transformation YAGNI Premature optimisation is the root of all evil
  59. 59. https://www.flickr.com/photos/38080114@N07/8594601982/
  60. 60. #15: Minimize Mutability
  61. 61. Rules to make a class immutable 1. Don’t provide any mutators 2. Ensure that the class can’t be extended 3. Make all fields final 4. Make all fields private 5. Ensure exclusive access to any mutable components
  62. 62. class ImmutableClass{ private final def field1 private final def field2 //... private final def field10 public ImmutableClass(f1, f2,… f10){ //initialization } }
  63. 63. import groovy.transform.Immutable @Immutable class Rectangle{ int length int breadth } def r = new Rectangle(length: 10, breadth: 5) println r // Rectangle(10, 5)
  64. 64. public Rectangle(java.util.HashMap args) { if ( args .length == null) { } else { this .length = (( args .length) as int) } if ( args .breadth == null) { } else { this .breadth = (( args .breadth) as int) } }
  65. 65. AST Transformation Readability Matters Syntactic Sugar
  66. 66. #15: Favour composition over inheritance
  67. 67. def ph = ['919812312345', '4512341234', ‘19252199916'] as PhoneNumbers println ph.find { it == '19252199916'} println ph.findAll { it.endsWith('4') }
  68. 68. def ph = ['919812312345', '4512341234', ‘19252199916'] as PhoneNumbers println ph.find { it == '19252199916'} println ph.findAll { it.endsWith('4') } println ph.indianNumbers()
  69. 69. class PhoneNumbers extends ArrayList { }
  70. 70. class PhoneNumbers { private @Delegate List phoneNumbers PhoneNumbers(numbers) { phoneNumbers = numbers } def indianNumbers() { phoneNumbers.findAll { it.startsWith('91') } } }
  71. 71. trait CanSing { def sing() { println "Singing" } } trait CanDance { def dance() { println "Dancing" } } class Person implements CanSing, CanDance {} Person reema = new Person() reema.sing() reema.dance()
  72. 72. AST Transformation Simplify
  73. 73. Take Away Some of the ‘Effective Java’ already built into the language AST Transformations reduce the effort to implement few more Effective Java implementations may not always be effective Groovy implementations (Traps)
  74. 74. Take Away Programming languages can reduce the friction to implement good practices The power of AST transformations is available to the developers
  75. 75. Thank You

×