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

Groovy in the light of Java 8 -- SpringOne2GX 2014

Groovy in the light of Java 8 -- SpringOne2GX 2014

How can Groovy developers can benefit from Java 8?
What are the differences between Java 8 and Groovy?
How does Groovy go beyond what Java 8?
Is Groovy still relevant now that we have Java 8?

Guillaume Laforge

September 09, 2014
Tweet

More Decks by Guillaume Laforge

Other Decks in Technology

Transcript

  1. © 2014 SpringOne 2GX. All rights reserved. Do not distribute

    without permission. Groovy, in the light of Java 8 Guillaume Laforge — Groovy project lead / Pivotal @glaforge
  2. But more precisely… • Will Java lambdas replace Groovy closures?

    • What are the differences between them? ! • Will Groovy support all the new Java 8 language constructs? • lambdas, default methods, method references… ! • How Groovy developers can benefit from Java 8? ! • What does Groovy offer beyond Java 8? 8
  3. What about redundancy? ! ! ! ! ! • Closures

    • Traits • Truth & null handling • Functional with collections • Method closures ! ! ! ! ! ! ! • Lambdas • Default methods • Optional • Stream API • Method references 9
  4. What we’re going to talk about • What’s new in

    Java 8 • new syntax constructs • new APIs ! • Similar concepts in Groovy • and how they compare or complement ! • What Groovy offers in addition • beyond Java, Groovy adds its own twist! 11
  5. © 2014 SpringOne 2GX. All rights reserved. Do not distribute

    without permission. What’s new in Java 8?
  6. This is not a Java 8 crash courses with all

    the details :-) We want to understand the implications with regards to Groovy
  7. What’s new in Java 8? ! • New syntax •

    Streams • Profiles • Security enhancements • JavaFX • Tools • i18n • Date / time API • Nashorn / JavaScript ! ! ! • Pack200 • IO / NIO improvements • New utility classes • JDBC • Networking • Concurrency • JAXP • Hotspot • Java Mission Control 14
  8. What’s new in Java 8? ! • New syntax •

    Streams • Profiles • Security enhancements • JavaFX • Tools • i18n • Date / time API • Nashorn / JavaScript ! ! ! • Pack200 • IO / NIO improvements • New utility classes • JDBC • Networking • Concurrency • JAXP • Hotspot • Java Mission Control 14 Great concise resource with the whole list: http://bit.ly/new-in-java-8
  9. © 2014 SpringOne 2GX. All rights reserved. Do not distribute

    without permission. Support the new Java 8 language features New Java 8 syntax
  10. New Java 8 syntax constructs • Lambda expressions • Method

    references • Static & default methods in interfaces • Repeating annotations • Annotations on types • Improved type inference • Method parameter reflection 16
  11. New Java 8 syntax constructs • Lambda expressions • Method

    references • Static & default methods in interfaces • Repeating annotations • Annotations on types • Improved type inference • Method parameter reflection 17
  12. Lambda expressions 19 double highestScore = students .filter(Student s ->

    s.getGradYear() == 2011) .map(Student s -> s.getScore()) .max();
  13. Lambda expressions 20 double highestScore = students .filter(Student s ->

    s.getGradYear() == 2011) .map(Student s -> s.getScore()) .max();
  14. Lambda expressions 20 double highestScore = students .filter(Student s ->

    s.getGradYear() == 2011) .map(Student s -> s.getScore()) .max(); Coercion into a Predicate « functional interface »
  15. Lambda expressions 20 double highestScore = students .filter(Student s ->

    s.getGradYear() == 2011) .map(Student s -> s.getScore()) .max(); There’s no function type!
  16. Lambda expressions 20 double highestScore = students .filter(Student s ->

    s.getGradYear() == 2011) .map(Student s -> s.getScore()) .max(); Pipeline being built Single pass!
  17. Lambda expressions 20 double highestScore = students .filter(Student s ->

    s.getGradYear() == 2011) .map(Student s -> s.getScore()) .max();
  18. Lambda expressions 21 double highestScore = students .findAll { it.gradYear

    == 2011 } .collect { it.score} .max() In Groovy, that would be…
  19. Lambda expressions 21 double highestScore = students .findAll { it.gradYear

    == 2011 } .collect { it.score} .max() Drawback: intermediary data structures Unless you use iterator variants
  20. The various lambda syntaxes 22 String name -> name.length() !

    (int left, int right) -> left + right ! (String left, String sep, String right) -> { System.out.println(left + sep + right) }
  21. The various lambda syntaxes 22 String name -> name.length() !

    (int left, int right) -> left + right ! (String left, String sep, String right) -> { System.out.println(left + sep + right) } One parameter: no parens Expression on right: no curly
  22. The various lambda syntaxes 22 String name -> name.length() !

    (int left, int right) -> left + right ! (String left, String sep, String right) -> { System.out.println(left + sep + right) } Parentheses required for more that one parameter
  23. The various lambda syntaxes 22 String name -> name.length() !

    (int left, int right) -> left + right ! (String left, String sep, String right) -> { System.out.println(left + sep + right) } Statements require curly braces Return keyword required if non-void returning
  24. The various lambda syntaxes 22 String name -> name.length() !

    (int left, int right) -> left + right ! (String left, String sep, String right) -> { System.out.println(left + sep + right) }
  25. The various lambda syntaxes 23 name -> name.length() ! (left,

    right) -> left + right ! (left, sep, right) -> { System.out.println(left + sep + right) }
  26. The various lambda syntaxes 23 name -> name.length() ! (left,

    right) -> left + right ! (left, sep, right) -> { System.out.println(left + sep + right) } Clever type inference can help get rid of parameter type declarations
  27. Closures vs lambdas 24 IntStream.range(1, 100).forEach(s -> System.out.println(s)); ! Files.lines(Paths.get('README.adoc'))

    .map(it -> it.toUpperCase()) .forEach(it -> System.out.println(it)); IntStream.range(1, 100).forEach { println it } ! Files.lines(Paths.get('README.adoc')) .map { it.toUpperCase() } .forEach { println it }
  28. Closures vs lambdas 24 IntStream.range(1, 100).forEach(s -> System.out.println(s)); ! Files.lines(Paths.get('README.adoc'))

    .map(it -> it.toUpperCase()) .forEach(it -> System.out.println(it)); IntStream.range(1, 100).forEach { println it } ! Files.lines(Paths.get('README.adoc')) .map { it.toUpperCase() } .forEach { println it } Use Groovy closures wherever you pass lambdas in Java 8
  29. Closures vs lambdas 24 IntStream.range(1, 100).forEach(s -> System.out.println(s)); ! Files.lines(Paths.get('README.adoc'))

    .map(it -> it.toUpperCase()) .forEach(it -> System.out.println(it)); IntStream.range(1, 100).forEach { println it } ! Files.lines(Paths.get('README.adoc')) .map { it.toUpperCase() } .forEach { println it } Groovy coerces to SAM types (Single Abstract Method)
  30. Closures vs lambdas 24 IntStream.range(1, 100).forEach(s -> System.out.println(s)); ! Files.lines(Paths.get('README.adoc'))

    .map(it -> it.toUpperCase()) .forEach(it -> System.out.println(it)); IntStream.range(1, 100).forEach { println it } ! Files.lines(Paths.get('README.adoc')) .map { it.toUpperCase() } .forEach { println it }
  31. Beyond: Closure default parameters 25 def mult = { int

    a, int b = 10 -> a * b } ! assert mult(2, 3) == 6 assert mult(5) == 50
  32. Beyond: Closure default parameters 25 def mult = { int

    a, int b = 10 -> a * b } ! assert mult(2, 3) == 6 assert mult(5) == 50 Default value if the parameter is not specified
  33. Beyond: Duck typing polymorphism 26 def adder = { a,

    b -> a + b } ! assert adder(100, 200) == 300 assert adder('X', 'Y') == 'XY'
  34. Beyond: Duck typing polymorphism 26 def adder = { a,

    b -> a + b } ! assert adder(100, 200) == 300 assert adder('X', 'Y') == 'XY' Works both for numbers and for strings
  35. Lambda-based builders uglier and less powerful 28 MarkupBuilder pom =

    new XmlMarkupBuilder() pom.el("modelVersion", "4.0.0"); pom.el("groupId", "com.github"); pom.el("artifactId", "lambda-builder"); pom.el("version", "1.0-SNAPSHOT"); pom.el("dependencies", () -> { pom.el("dependency", () -> { pom.el("groupId", "junit"); pom.el("artifactId", "junit"); pom.elx("version", "4.11"); }); pom.el("dependency", () -> { pom.el("groupId", "commons-beanutils"); pom.el("artifactId", "commons-beanutils"); pom.elx("version", "1.7.0"); }); });
  36. Lambda-based builders uglier and less powerful 28 MarkupBuilder pom =

    new XmlMarkupBuilder() pom.el("modelVersion", "4.0.0"); pom.el("groupId", "com.github"); pom.el("artifactId", "lambda-builder"); pom.el("version", "1.0-SNAPSHOT"); pom.el("dependencies", () -> { pom.el("dependency", () -> { pom.el("groupId", "junit"); pom.el("artifactId", "junit"); pom.elx("version", "4.11"); }); pom.el("dependency", () -> { pom.el("groupId", "commons-beanutils"); pom.el("artifactId", "commons-beanutils"); pom.elx("version", "1.7.0"); }); }); Repeated « pom »: No delegate like in Groovy’s closures
  37. Lambda-based builders uglier and less powerful 28 MarkupBuilder pom =

    new XmlMarkupBuilder() pom.el("modelVersion", "4.0.0"); pom.el("groupId", "com.github"); pom.el("artifactId", "lambda-builder"); pom.el("version", "1.0-SNAPSHOT"); pom.el("dependencies", () -> { pom.el("dependency", () -> { pom.el("groupId", "junit"); pom.el("artifactId", "junit"); pom.elx("version", "4.11"); }); pom.el("dependency", () -> { pom.el("groupId", "commons-beanutils"); pom.el("artifactId", "commons-beanutils"); pom.elx("version", "1.7.0"); }); }); Zero-arg lamdas not as lean
  38. Lambda-based builders uglier and less powerful 28 MarkupBuilder pom =

    new XmlMarkupBuilder() pom.el("modelVersion", "4.0.0"); pom.el("groupId", "com.github"); pom.el("artifactId", "lambda-builder"); pom.el("version", "1.0-SNAPSHOT"); pom.el("dependencies", () -> { pom.el("dependency", () -> { pom.el("groupId", "junit"); pom.el("artifactId", "junit"); pom.elx("version", "4.11"); }); pom.el("dependency", () -> { pom.el("groupId", "commons-beanutils"); pom.el("artifactId", "commons-beanutils"); pom.elx("version", "1.7.0"); }); }); Generic method + string: No dynamic method
  39. Neater Groovy builder! 29 def pom = new PomBuilder().project {

    modelVersion "4.0.0" groupId "com.github" artifactId "lambda-builder" version "1.0-SNAPSHOT" dependencies { dependency { groupId "junit" artifactId "junit" version "4.11" } dependency { groupId "commons-beanutils" artifactId "commons-beanutils" version "1.7.0" } } }
  40. Neater Groovy builder! 29 def pom = new PomBuilder().project {

    modelVersion "4.0.0" groupId "com.github" artifactId "lambda-builder" version "1.0-SNAPSHOT" dependencies { dependency { groupId "junit" artifactId "junit" version "4.11" } dependency { groupId "commons-beanutils" artifactId "commons-beanutils" version "1.7.0" } } }
  41. Closure and method memoization 33 def fib2 = { long

    n -> if (n < 2) 1 else call(n - 1) + call(n - 2) }.memoize()
  42. Closure and method memoization 33 def fib2 = { long

    n -> if (n < 2) 1 else call(n - 1) + call(n - 2) }.memoize() Closures: memoize()
  43. Closure and method memoization 33 @Memoized long fib(long n) {

    if (n < 2) 1 else fib(n - 1) + fib(n - 2) } def fib2 = { long n -> if (n < 2) 1 else call(n - 1) + call(n - 2) }.memoize() Closures: memoize()
  44. Closure and method memoization 33 @Memoized long fib(long n) {

    if (n < 2) 1 else fib(n - 1) + fib(n - 2) } def fib2 = { long n -> if (n < 2) 1 else call(n - 1) + call(n - 2) }.memoize() Closures: memoize() Methods: memoization AST transformation
  45. Closure and method tail recursion 35 def fact = {

    n, accu = 1G -> if (n < 2) accu else fact.trampoline(n - 1, n * accu) }.trampoline()
  46. Closure and method tail recursion 35 def fact = {

    n, accu = 1G -> if (n < 2) accu else fact.trampoline(n - 1, n * accu) }.trampoline() Closures: Tail recursion with trampoline()
  47. Closure and method tail recursion 35 @groovy.transform.TailRecursive def fact(n, accu

    = 1G) { if (n < 2) accu else fact(n - 1, n * accu) } def fact = { n, accu = 1G -> if (n < 2) accu else fact.trampoline(n - 1, n * accu) }.trampoline() Closures: Tail recursion with trampoline()
  48. Closure and method tail recursion 35 @groovy.transform.TailRecursive def fact(n, accu

    = 1G) { if (n < 2) accu else fact(n - 1, n * accu) } def fact = { n, accu = 1G -> if (n < 2) accu else fact.trampoline(n - 1, n * accu) }.trampoline() Closures: Tail recursion with trampoline() Methods: Tail recursion with @TailRecursive transformation
  49. Method references — 3 main cases 37 instance::instanceMethod ! SomeClass::staticMethod

    ! SomeClass::instanceMethod Not covered by Groovy method closures yet!
  50. Static methods in interfaces • You can put static utility

    methods in interfaces, 
 instead of in companion classes (like « Collections ») 39 public interface Stream<T> { // ... static <T> Stream<T> empty() { return new Stream<T> { ... } } }
  51. Default methods in interfaces • Define default behavior • possibly

    to enrich existing interfaces 40 public interface Stream<T> { // ... default Builder<T> add(T t) { ... } }
  52. Groovy Traits • Like interfaces, but with method bodies •

    similar to Java 8 interface default methods • Elegant way to compose behavior • multiple inheritance without the « diamond » problem • Traits can also be stateful • traits can have properties like normal classes • Compatible with static typing and static compilation • class methods from traits also visible from Java classes • Also possible to implement traits at runtime 41
  53. Simple trait 42 trait FlyingAbility { String fly() { "I'm

    flying!" } } ! class Bird implements FlyingAbility {} def b = new Bird() ! assert b.fly() == "I'm flying!"
  54. Trait with state 43 trait Named { String name }

    ! class Bird implements Named {} def b = new Bird(name: 'Colibri') ! assert b.name == 'Colibri'
  55. Multiple inheritance 44 trait KiteSurfer { String surf() { 'kite'

    } } ! trait WebSurfer { String surf() { 'web' } } ! class Person { String name } ! class Hipster extends Person implements KiteSurfer, WebSurfer {} ! def h = new Hipster() assert h.surf() == 'web'
  56. To know all about traits! 45 Rethinking API 
 design

    with traits by Cédric Champeau Tue 2:30pm / Trinity 3
  57. Repeating annotations 47 @Schedule(dayOfMonth = "last") @Schedule(dayOfWeek = "Fri", hour

    = 23) public void doPeriodicCleanup() { ... } @Schedule annotation repeated twice
  58. Repeating annotations 47 @Schedule(dayOfMonth = "last") @Schedule(dayOfWeek = "Fri", hour

    = 23) public void doPeriodicCleanup() { ... } @Repeatable(Schedules.class) public @interface Schedule { String dayOfMonth() default "first"; String dayOfWeek() default "Mon"; int hour() default 12; }
  59. Repeating annotations 47 @Schedule(dayOfMonth = "last") @Schedule(dayOfWeek = "Fri", hour

    = 23) public void doPeriodicCleanup() { ... } @Repeatable(Schedules.class) public @interface Schedule { String dayOfMonth() default "first"; String dayOfWeek() default "Mon"; int hour() default 12; } Container annotation for the repeated annotationd
  60. Repeating annotations 47 @Schedule(dayOfMonth = "last") @Schedule(dayOfWeek = "Fri", hour

    = 23) public void doPeriodicCleanup() { ... } @Repeatable(Schedules.class) public @interface Schedule { String dayOfMonth() default "first"; String dayOfWeek() default "Mon"; int hour() default 12; } public @interface Schedules { Schedule[] value(); }
  61. Repeating annotations 47 @Schedule(dayOfMonth = "last") @Schedule(dayOfWeek = "Fri", hour

    = 23) public void doPeriodicCleanup() { ... } @Repeatable(Schedules.class) public @interface Schedule { String dayOfMonth() default "first"; String dayOfWeek() default "Mon"; int hour() default 12; } public @interface Schedules { Schedule[] value(); } The container annotation iteself
  62. Repeating annotations 47 @Schedule(dayOfMonth = "last") @Schedule(dayOfWeek = "Fri", hour

    = 23) public void doPeriodicCleanup() { ... } @Repeatable(Schedules.class) public @interface Schedule { String dayOfMonth() default "first"; String dayOfWeek() default "Mon"; int hour() default 12; } public @interface Schedules { Schedule[] value(); }
  63. Repeating annotations 47 @Schedule(dayOfMonth = "last") @Schedule(dayOfWeek = "Fri", hour

    = 23) public void doPeriodicCleanup() { ... } @Repeatable(Schedules.class) public @interface Schedule { String dayOfMonth() default "first"; String dayOfWeek() default "Mon"; int hour() default 12; } public @interface Schedules { Schedule[] value(); } Not yet supported
  64. Repeating annotations 47 @Schedule(dayOfMonth = "last") @Schedule(dayOfWeek = "Fri", hour

    = 23) public void doPeriodicCleanup() { ... } @Repeatable(Schedules.class) public @interface Schedule { String dayOfMonth() default "first"; String dayOfWeek() default "Mon"; int hour() default 12; } public @interface Schedules { Schedule[] value(); }
  65. Annotations on types • Everywhere you can put a type,

    you can put an annotation 48 @NonNull String name; ! email = (@Email String) input; ! List<@NonNull String> names; ! new @Interned MyObject(); ! void monitorTemperature() throws @Critical TemperatureException { ... } ! class UnmodifiableList<T> implements @Readonly List<@Readonly T> {...}
  66. Annotations on types • Everywhere you can put a type,

    you can put an annotation 48 @NonNull String name; ! email = (@Email String) input; ! List<@NonNull String> names; ! new @Interned MyObject(); ! void monitorTemperature() throws @Critical TemperatureException { ... } ! class UnmodifiableList<T> implements @Readonly List<@Readonly T> {...} Not yet supported
  67. Annotations on types • Everywhere you can put a type,

    you can put an annotation 48 @NonNull String name; ! email = (@Email String) input; ! List<@NonNull String> names; ! new @Interned MyObject(); ! void monitorTemperature() throws @Critical TemperatureException { ... } ! class UnmodifiableList<T> implements @Readonly List<@Readonly T> {...} Imagine the potential for targets for local AST transformations?
  68. Annotations on types • Everywhere you can put a type,

    you can put an annotation 48 @NonNull String name; ! email = (@Email String) input; ! List<@NonNull String> names; ! new @Interned MyObject(); ! void monitorTemperature() throws @Critical TemperatureException { ... } ! class UnmodifiableList<T> implements @Readonly List<@Readonly T> {...}
  69. Groovy compile-time meta-annotations 49 @Service @Transactional class MyTransactionalService {} import

    groovy.transform.AnnotationCollector ! @Service @Transactional @AnnotationCollector public @interface TransactionalService {}
  70. Groovy compile-time meta-annotations 49 import groovy.transform.AnnotationCollector ! @Service @Transactional @AnnotationCollector

    public @interface TransactionalService {} ! @TransactionalService class MyTransactionalService {}
  71. Groovy compile-time meta-annotations 49 import groovy.transform.AnnotationCollector ! @Service @Transactional @AnnotationCollector

    public @interface TransactionalService {} ! @TransactionalService class MyTransactionalService {} Can handle parameters (even conflicting), or you can create your own « processor »
  72. Groovy compile-time meta-annotations 49 import groovy.transform.AnnotationCollector ! @Service @Transactional @AnnotationCollector

    public @interface TransactionalService {} ! @TransactionalService class MyTransactionalService {}
  73. Date / Time API 52 LocalDate.now(); today.with(TemporalAdjusters.lastDayOfMonth()).minusDays(2); ! LocalDate.of(2012, Month.MAY,

    14); dateOfBirth.plusYears(1); ! LocalDate date = LocalDate.of(2000, Month.NOVEMBER, 20); LocalDate nextWed = date.with(TemporalAdjusters.next(DayOfWeek.WEDNESDAY));
  74. Date / Time API 52 LocalDate.now(); today.with(TemporalAdjusters.lastDayOfMonth()).minusDays(2); ! LocalDate.of(2012, Month.MAY,

    14); dateOfBirth.plusYears(1); ! LocalDate date = LocalDate.of(2000, Month.NOVEMBER, 20); LocalDate nextWed = date.with(TemporalAdjusters.next(DayOfWeek.WEDNESDAY)); Groovy could add some operator overloading?
  75. Date / Time API 52 LocalDate.now(); today.with(TemporalAdjusters.lastDayOfMonth()).minusDays(2); ! LocalDate.of(2012, Month.MAY,

    14); dateOfBirth.plusYears(1); ! LocalDate date = LocalDate.of(2000, Month.NOVEMBER, 20); LocalDate nextWed = date.with(TemporalAdjusters.next(DayOfWeek.WEDNESDAY));
  76. Groovy’s date / time handling 53 import static java.util.Calendar.* import

    groovy.time.* import org.codehaus.groovy.runtime.TimeCategory ! def cal = Calendar.instance cal.set(year: 2010, month: JULY, date: 9) ! assert FRIDAY == cal[DAY_OF_WEEK] ! use (TimeCategory) { 2.years + 3.months + 15.days + 23.minutes + 2.seconds ! 1.week - 1.day new Date() + 6.days ! 3.days.ago new Date() - 3 }
  77. Groovy’s date / time handling 53 import static java.util.Calendar.* import

    groovy.time.* import org.codehaus.groovy.runtime.TimeCategory ! def cal = Calendar.instance cal.set(year: 2010, month: JULY, date: 9) ! assert FRIDAY == cal[DAY_OF_WEEK] ! use (TimeCategory) { 2.years + 3.months + 15.days + 23.minutes + 2.seconds ! 1.week - 1.day new Date() + 6.days ! 3.days.ago new Date() - 3 } Groovy could provide the same for Date / Time
  78. Groovy’s date / time handling 53 import static java.util.Calendar.* import

    groovy.time.* import org.codehaus.groovy.runtime.TimeCategory ! def cal = Calendar.instance cal.set(year: 2010, month: JULY, date: 9) ! assert FRIDAY == cal[DAY_OF_WEEK] ! use (TimeCategory) { 2.years + 3.months + 15.days + 23.minutes + 2.seconds ! 1.week - 1.day new Date() + 6.days ! 3.days.ago new Date() - 3 }
  79. map map map map map reduce reduce reduce reduce reduce

    Map / filter / reduce explained to your 6 year old
  80. Stream 56 persons.stream() .filter( p -> p.getAge() < 18 )

    .map( p -> p.getName().toUpperCase() ) .sorted() .collect(Collectors.joining(", "));
  81. Groovy’s functional style with the GDK methods 57 persons .findAll

    { it.getAge() < 18 } .collect { it.name.toUpperCase() } .sort() .joining(", ")
  82. Groovy using streams too! 58 persons.stream() .filter { it.age <

    18 } .map { it.name.toUpperCase() } .sorted() .collect(Collectors.joining(", "))
  83. Groovy using streams too! 58 persons.stream() .filter { it.age <

    18 } .map { it.name.toUpperCase() } .sorted() .collect(Collectors.joining(", ")) Leveraging closure to « SAM » type coercion
  84. Groovy using streams too! 58 persons.stream() .filter { it.age <

    18 } .map { it.name.toUpperCase() } .sorted() .collect(Collectors.joining(", "))
  85. Optional 59 Optional<String> maybeName = Optional.of("Guillaume"); ! String result =

    maybeName.orElse("unknown") ! if (maybeName.ifPresent()) { System.out.println(maybeName.get()); } else { System.out.println("unknown"); }
  86. Optional 59 Optional<String> maybeName = Optional.of("Guillaume"); ! String result =

    maybeName.orElse("unknown") ! if (maybeName.ifPresent()) { System.out.println(maybeName.get()); } else { System.out.println("unknown"); } Wrap something that can be potentially null
  87. Optional 59 Optional<String> maybeName = Optional.of("Guillaume"); ! String result =

    maybeName.orElse("unknown") ! if (maybeName.ifPresent()) { System.out.println(maybeName.get()); } else { System.out.println("unknown"); }
  88. Optional 59 Optional<String> maybeName = Optional.of("Guillaume"); ! String result =

    maybeName.orElse("unknown") ! if (maybeName.ifPresent()) { System.out.println(maybeName.get()); } else { System.out.println("unknown"); } Force handling the null value
  89. Optional 59 Optional<String> maybeName = Optional.of("Guillaume"); ! String result =

    maybeName.orElse("unknown") ! if (maybeName.ifPresent()) { System.out.println(maybeName.get()); } else { System.out.println("unknown"); }
  90. You know you can customize the truth in Groovy? Just

    implement a custom asBoolean() method!
  91. The law of Groovy Truth 61 Everything that’s null, empty,

    zero-sized, equal to zero is false Otherwise, it’s true!
  92. ?:

  93. Groovy truth, null handling, Elvis with Optional 63 def maybeName

    = Optional.of("Guillaume") ! def result = maybeName ?: "unknown" ! if (maybeName) { println maybeName.get() } else { println "unknown" } ! maybeName?.toUpperCase()
  94. Groovy truth, null handling, Elvis with Optional 63 def maybeName

    = Optional.of("Guillaume") ! def result = maybeName ?: "unknown" ! if (maybeName) { println maybeName.get() } else { println "unknown" } ! maybeName?.toUpperCase() Elvis!
  95. Groovy truth, null handling, Elvis with Optional 63 def maybeName

    = Optional.of("Guillaume") ! def result = maybeName ?: "unknown" ! if (maybeName) { println maybeName.get() } else { println "unknown" } ! maybeName?.toUpperCase() Groovy Truth!
  96. Groovy truth, null handling, Elvis with Optional 63 def maybeName

    = Optional.of("Guillaume") ! def result = maybeName ?: "unknown" ! if (maybeName) { println maybeName.get() } else { println "unknown" } ! maybeName?.toUpperCase() Safe navigation
  97. Groovy truth, null handling, Elvis with Optional 63 def maybeName

    = Optional.of("Guillaume") ! def result = maybeName ?: "unknown" ! if (maybeName) { println maybeName.get() } else { println "unknown" } ! maybeName?.toUpperCase() Interesting discussions on the mailing-list to further enhance usage of Optional from Groovy
  98. Groovy truth, null handling, Elvis with Optional 63 def maybeName

    = Optional.of("Guillaume") ! def result = maybeName ?: "unknown" ! if (maybeName) { println maybeName.get() } else { println "unknown" } ! maybeName?.toUpperCase() Not Yet Implemented
  99. Groovy truth, null handling, Elvis with Optional 63 def maybeName

    = Optional.of("Guillaume") ! def result = maybeName ?: "unknown" ! if (maybeName) { println maybeName.get() } else { println "unknown" } ! maybeName?.toUpperCase()
  100. Call JavaScript from Groovy with JSR-223 65 def manager =

    new ScriptEngineManager() def engine = manager.getEngineByName("nashorn") ! assert engine.eval("{} + []") == 0
  101. Call JavaScript from Groovy with JSR-223 65 def manager =

    new ScriptEngineManager() def engine = manager.getEngineByName("nashorn") ! assert engine.eval("{} + []") == 0 Sane JavaScript logic :-)
  102. GroovyFX 70 import javafx.scene.Scene import static groovyx.javafx.GroovyFX.start ! def chamber

    = ["5 Stelle": 108, "Italia.\nBene commune": 340, "Con Monti per l'Italia": 45, "Berlusconi": 124, "others": 4] ! start { stage(title: 'Italian chamber of Deputies', width: 1024, height: 700, visible: true) { Scene s = scene { tilePane { barChart(barGap: 10, categoryGap: 20, title: "Italy's election in February 2013") { series(name: 'Chamber (seats)', data: ['5 Stelle', 17.5, 'Monti', 7.8, 'Bene commune', 55, 'Berlusconi', 19.9]) series(name: 'Senate (seats)', data: ['5 Stelle', 17.4, 'Monti', 6.1, 'Bene commune', 39.1, 'Berlusconi', 37.1]) } pieChart(data: chamber, title: "Chamber of Deputies") } } s.stylesheets.add("Chart.css") } }
  103. GroovyFX 70 import javafx.scene.Scene import static groovyx.javafx.GroovyFX.start ! def chamber

    = ["5 Stelle": 108, "Italia.\nBene commune": 340, "Con Monti per l'Italia": 45, "Berlusconi": 124, "others": 4] ! start { stage(title: 'Italian chamber of Deputies', width: 1024, height: 700, visible: true) { Scene s = scene { tilePane { barChart(barGap: 10, categoryGap: 20, title: "Italy's election in February 2013") { series(name: 'Chamber (seats)', data: ['5 Stelle', 17.5, 'Monti', 7.8, 'Bene commune', 55, 'Berlusconi', 19.9]) series(name: 'Senate (seats)', data: ['5 Stelle', 17.4, 'Monti', 6.1, 'Bene commune', 39.1, 'Berlusconi', 37.1]) } pieChart(data: chamber, title: "Chamber of Deputies") } } s.stylesheets.add("Chart.css") } } Groovy builder to the rescue!
  104. Scripting 73 @Grab('net.sourceforge.htmlcleaner:htmlcleaner:2.4') import org.htmlcleaner.* ! def src = new

    File('html').toPath() def dst = new File('asciidoc').toPath() ! def cleaner = new HtmlCleaner() def props = cleaner.properties props.translateSpecialEntities = false def serializer = new SimpleHtmlSerializer(props) ! src.toFile().eachFileRecurse { f -> def relative = src.relativize(f.toPath()) def target = dst.resolve(relative) if (f.isDirectory()) { target.toFile().mkdir() } else if (f.name.endsWith('.html')) { def tmpHtml = File.createTempFile('clean', 'html') println "Converting $relative" def result = cleaner.clean(f) result.traverse({ tagNode, htmlNode -> tagNode?.attributes?.remove 'class' if ('td' == tagNode?.name || 'th'==tagNode?.name) { tagNode.name='td' String txt = tagNode.text tagNode.removeAllChildren() tagNode.insertChild(0, new ContentNode(txt)) } true } as TagNodeVisitor) serializer.writeToFile( result, tmpHtml.absolutePath, "utf-8" ) "pandoc -f html -t asciidoc -R -S --normalize -s $tmpHtml -o ${target}.adoc" .execute().waitFor() tmpHtml.delete() } } <html/> AsciiDoctor
  105. Ant scripting 74 @Grab('net.sourceforge.htmlcleaner:htmlcleaner:2.4') import org.htmlcleaner.* ! def src =

    new File('html').toPath() def dst = new File('asciidoc').toPath() ! def cleaner = new HtmlCleaner() def props = cleaner.properties props.translateSpecialEntities = false def serializer = new SimpleHtmlSerializer(props) ! src.toFile().eachFileRecurse { f -> def relative = src.relativize(f.toPath()) def target = dst.resolve(relative) if (f.isDirectory()) { target.toFile().mkdir() } else if (f.name.endsWith('.html')) { def tmpHtml = File.createTempFile('clean', 'html') println "Converting $relative" def result = cleaner.clean(f) result.traverse({ tagNode, htmlNode -> tagNode?.attributes?.remove 'class' if ('td' == tagNode?.name || 'th'==tagNode?.name) { tagNode.name='td'
  106. Ant scripting 74 @Grab('net.sourceforge.htmlcleaner:htmlcleaner:2.4') import org.htmlcleaner.* ! def src =

    new File('html').toPath() def dst = new File('asciidoc').toPath() ! def cleaner = new HtmlCleaner() def props = cleaner.properties props.translateSpecialEntities = false def serializer = new SimpleHtmlSerializer(props) ! src.toFile().eachFileRecurse { f -> def relative = src.relativize(f.toPath()) def target = dst.resolve(relative) if (f.isDirectory()) { target.toFile().mkdir() } else if (f.name.endsWith('.html')) { def tmpHtml = File.createTempFile('clean', 'html') println "Converting $relative" def result = cleaner.clean(f) result.traverse({ tagNode, htmlNode -> tagNode?.attributes?.remove 'class' if ('td' == tagNode?.name || 'th'==tagNode?.name) { tagNode.name='td' Use @Grab to download the htmlcleaner library
  107. Ant scripting 74 @Grab('net.sourceforge.htmlcleaner:htmlcleaner:2.4') import org.htmlcleaner.* ! def src =

    new File('html').toPath() def dst = new File('asciidoc').toPath() ! def cleaner = new HtmlCleaner() def props = cleaner.properties props.translateSpecialEntities = false def serializer = new SimpleHtmlSerializer(props) ! src.toFile().eachFileRecurse { f -> def relative = src.relativize(f.toPath()) def target = dst.resolve(relative) if (f.isDirectory()) { target.toFile().mkdir() } else if (f.name.endsWith('.html')) { def tmpHtml = File.createTempFile('clean', 'html') println "Converting $relative" def result = cleaner.clean(f) result.traverse({ tagNode, htmlNode -> tagNode?.attributes?.remove 'class' if ('td' == tagNode?.name || 'th'==tagNode?.name) { tagNode.name='td'
  108. Ant scripting 74 @Grab('net.sourceforge.htmlcleaner:htmlcleaner:2.4') import org.htmlcleaner.* ! def src =

    new File('html').toPath() def dst = new File('asciidoc').toPath() ! def cleaner = new HtmlCleaner() def props = cleaner.properties props.translateSpecialEntities = false def serializer = new SimpleHtmlSerializer(props) ! src.toFile().eachFileRecurse { f -> def relative = src.relativize(f.toPath()) def target = dst.resolve(relative) if (f.isDirectory()) { target.toFile().mkdir() } else if (f.name.endsWith('.html')) { def tmpHtml = File.createTempFile('clean', 'html') println "Converting $relative" def result = cleaner.clean(f) result.traverse({ tagNode, htmlNode -> tagNode?.attributes?.remove 'class' if ('td' == tagNode?.name || 'th'==tagNode?.name) { tagNode.name='td' Setup HtmlCleaner
  109. Ant scripting 74 @Grab('net.sourceforge.htmlcleaner:htmlcleaner:2.4') import org.htmlcleaner.* ! def src =

    new File('html').toPath() def dst = new File('asciidoc').toPath() ! def cleaner = new HtmlCleaner() def props = cleaner.properties props.translateSpecialEntities = false def serializer = new SimpleHtmlSerializer(props) ! src.toFile().eachFileRecurse { f -> def relative = src.relativize(f.toPath()) def target = dst.resolve(relative) if (f.isDirectory()) { target.toFile().mkdir() } else if (f.name.endsWith('.html')) { def tmpHtml = File.createTempFile('clean', 'html') println "Converting $relative" def result = cleaner.clean(f) result.traverse({ tagNode, htmlNode -> tagNode?.attributes?.remove 'class' if ('td' == tagNode?.name || 'th'==tagNode?.name) { tagNode.name='td'
  110. Ant scripting 74 @Grab('net.sourceforge.htmlcleaner:htmlcleaner:2.4') import org.htmlcleaner.* ! def src =

    new File('html').toPath() def dst = new File('asciidoc').toPath() ! def cleaner = new HtmlCleaner() def props = cleaner.properties props.translateSpecialEntities = false def serializer = new SimpleHtmlSerializer(props) ! src.toFile().eachFileRecurse { f -> def relative = src.relativize(f.toPath()) def target = dst.resolve(relative) if (f.isDirectory()) { target.toFile().mkdir() } else if (f.name.endsWith('.html')) { def tmpHtml = File.createTempFile('clean', 'html') println "Converting $relative" def result = cleaner.clean(f) result.traverse({ tagNode, htmlNode -> tagNode?.attributes?.remove 'class' if ('td' == tagNode?.name || 'th'==tagNode?.name) { tagNode.name='td' Recursively find all the files to transform
  111. Ant scripting 74 @Grab('net.sourceforge.htmlcleaner:htmlcleaner:2.4') import org.htmlcleaner.* ! def src =

    new File('html').toPath() def dst = new File('asciidoc').toPath() ! def cleaner = new HtmlCleaner() def props = cleaner.properties props.translateSpecialEntities = false def serializer = new SimpleHtmlSerializer(props) ! src.toFile().eachFileRecurse { f -> def relative = src.relativize(f.toPath()) def target = dst.resolve(relative) if (f.isDirectory()) { target.toFile().mkdir() } else if (f.name.endsWith('.html')) { def tmpHtml = File.createTempFile('clean', 'html') println "Converting $relative" def result = cleaner.clean(f) result.traverse({ tagNode, htmlNode -> tagNode?.attributes?.remove 'class' if ('td' == tagNode?.name || 'th'==tagNode?.name) { tagNode.name='td'
  112. Ant scripting 75 def props = cleaner.properties props.translateSpecialEntities = false

    def serializer = new SimpleHtmlSerializer(props) ! src.toFile().eachFileRecurse { f -> def relative = src.relativize(f.toPath()) def target = dst.resolve(relative) if (f.isDirectory()) { target.toFile().mkdir() } else if (f.name.endsWith('.html')) { def tmpHtml = File.createTempFile('clean', 'html') println "Converting $relative" def result = cleaner.clean(f) result.traverse({ tagNode, htmlNode -> tagNode?.attributes?.remove 'class' if ('td' == tagNode?.name || 'th'==tagNode?.name) { tagNode.name='td' String txt = tagNode.text tagNode.removeAllChildren() tagNode.insertChild(0, new ContentNode(txt)) } true } as TagNodeVisitor) serializer.writeToFile( result, tmpHtml.absolutePath, "utf-8" )
  113. Ant scripting 75 def props = cleaner.properties props.translateSpecialEntities = false

    def serializer = new SimpleHtmlSerializer(props) ! src.toFile().eachFileRecurse { f -> def relative = src.relativize(f.toPath()) def target = dst.resolve(relative) if (f.isDirectory()) { target.toFile().mkdir() } else if (f.name.endsWith('.html')) { def tmpHtml = File.createTempFile('clean', 'html') println "Converting $relative" def result = cleaner.clean(f) result.traverse({ tagNode, htmlNode -> tagNode?.attributes?.remove 'class' if ('td' == tagNode?.name || 'th'==tagNode?.name) { tagNode.name='td' String txt = tagNode.text tagNode.removeAllChildren() tagNode.insertChild(0, new ContentNode(txt)) } true } as TagNodeVisitor) serializer.writeToFile( result, tmpHtml.absolutePath, "utf-8" ) Clean the HTML
  114. Ant scripting 75 def props = cleaner.properties props.translateSpecialEntities = false

    def serializer = new SimpleHtmlSerializer(props) ! src.toFile().eachFileRecurse { f -> def relative = src.relativize(f.toPath()) def target = dst.resolve(relative) if (f.isDirectory()) { target.toFile().mkdir() } else if (f.name.endsWith('.html')) { def tmpHtml = File.createTempFile('clean', 'html') println "Converting $relative" def result = cleaner.clean(f) result.traverse({ tagNode, htmlNode -> tagNode?.attributes?.remove 'class' if ('td' == tagNode?.name || 'th'==tagNode?.name) { tagNode.name='td' String txt = tagNode.text tagNode.removeAllChildren() tagNode.insertChild(0, new ContentNode(txt)) } true } as TagNodeVisitor) serializer.writeToFile( result, tmpHtml.absolutePath, "utf-8" )
  115. Ant scripting 75 def props = cleaner.properties props.translateSpecialEntities = false

    def serializer = new SimpleHtmlSerializer(props) ! src.toFile().eachFileRecurse { f -> def relative = src.relativize(f.toPath()) def target = dst.resolve(relative) if (f.isDirectory()) { target.toFile().mkdir() } else if (f.name.endsWith('.html')) { def tmpHtml = File.createTempFile('clean', 'html') println "Converting $relative" def result = cleaner.clean(f) result.traverse({ tagNode, htmlNode -> tagNode?.attributes?.remove 'class' if ('td' == tagNode?.name || 'th'==tagNode?.name) { tagNode.name='td' String txt = tagNode.text tagNode.removeAllChildren() tagNode.insertChild(0, new ContentNode(txt)) } true } as TagNodeVisitor) serializer.writeToFile( result, tmpHtml.absolutePath, "utf-8" ) Some more clean-up
  116. Ant scripting 75 def props = cleaner.properties props.translateSpecialEntities = false

    def serializer = new SimpleHtmlSerializer(props) ! src.toFile().eachFileRecurse { f -> def relative = src.relativize(f.toPath()) def target = dst.resolve(relative) if (f.isDirectory()) { target.toFile().mkdir() } else if (f.name.endsWith('.html')) { def tmpHtml = File.createTempFile('clean', 'html') println "Converting $relative" def result = cleaner.clean(f) result.traverse({ tagNode, htmlNode -> tagNode?.attributes?.remove 'class' if ('td' == tagNode?.name || 'th'==tagNode?.name) { tagNode.name='td' String txt = tagNode.text tagNode.removeAllChildren() tagNode.insertChild(0, new ContentNode(txt)) } true } as TagNodeVisitor) serializer.writeToFile( result, tmpHtml.absolutePath, "utf-8" )
  117. Ant scripting 76 def target = dst.resolve(relative) if (f.isDirectory()) {

    target.toFile().mkdir() } else if (f.name.endsWith('.html')) { def tmpHtml = File.createTempFile('clean', 'html') println "Converting $relative" def result = cleaner.clean(f) result.traverse({ tagNode, htmlNode -> tagNode?.attributes?.remove 'class' if ('td' == tagNode?.name || 'th'==tagNode?.name) { tagNode.name='td' String txt = tagNode.text tagNode.removeAllChildren() tagNode.insertChild(0, new ContentNode(txt)) } true } as TagNodeVisitor) serializer.writeToFile( result, tmpHtml.absolutePath, "utf-8" ) "pandoc -f html -t asciidoc -R -S --normalize -s $tmpHtml -o ${target}.adoc" .execute().waitFor() tmpHtml.delete() } }
  118. Ant scripting 76 def target = dst.resolve(relative) if (f.isDirectory()) {

    target.toFile().mkdir() } else if (f.name.endsWith('.html')) { def tmpHtml = File.createTempFile('clean', 'html') println "Converting $relative" def result = cleaner.clean(f) result.traverse({ tagNode, htmlNode -> tagNode?.attributes?.remove 'class' if ('td' == tagNode?.name || 'th'==tagNode?.name) { tagNode.name='td' String txt = tagNode.text tagNode.removeAllChildren() tagNode.insertChild(0, new ContentNode(txt)) } true } as TagNodeVisitor) serializer.writeToFile( result, tmpHtml.absolutePath, "utf-8" ) "pandoc -f html -t asciidoc -R -S --normalize -s $tmpHtml -o ${target}.adoc" .execute().waitFor() tmpHtml.delete() } } Write the result
  119. Ant scripting 76 def target = dst.resolve(relative) if (f.isDirectory()) {

    target.toFile().mkdir() } else if (f.name.endsWith('.html')) { def tmpHtml = File.createTempFile('clean', 'html') println "Converting $relative" def result = cleaner.clean(f) result.traverse({ tagNode, htmlNode -> tagNode?.attributes?.remove 'class' if ('td' == tagNode?.name || 'th'==tagNode?.name) { tagNode.name='td' String txt = tagNode.text tagNode.removeAllChildren() tagNode.insertChild(0, new ContentNode(txt)) } true } as TagNodeVisitor) serializer.writeToFile( result, tmpHtml.absolutePath, "utf-8" ) "pandoc -f html -t asciidoc -R -S --normalize -s $tmpHtml -o ${target}.adoc" .execute().waitFor() tmpHtml.delete() } }
  120. Ant scripting 76 def target = dst.resolve(relative) if (f.isDirectory()) {

    target.toFile().mkdir() } else if (f.name.endsWith('.html')) { def tmpHtml = File.createTempFile('clean', 'html') println "Converting $relative" def result = cleaner.clean(f) result.traverse({ tagNode, htmlNode -> tagNode?.attributes?.remove 'class' if ('td' == tagNode?.name || 'th'==tagNode?.name) { tagNode.name='td' String txt = tagNode.text tagNode.removeAllChildren() tagNode.insertChild(0, new ContentNode(txt)) } true } as TagNodeVisitor) serializer.writeToFile( result, tmpHtml.absolutePath, "utf-8" ) "pandoc -f html -t asciidoc -R -S --normalize -s $tmpHtml -o ${target}.adoc" .execute().waitFor() tmpHtml.delete() } } Call an external process to convert and wait for its execution
  121. Ant scripting 76 def target = dst.resolve(relative) if (f.isDirectory()) {

    target.toFile().mkdir() } else if (f.name.endsWith('.html')) { def tmpHtml = File.createTempFile('clean', 'html') println "Converting $relative" def result = cleaner.clean(f) result.traverse({ tagNode, htmlNode -> tagNode?.attributes?.remove 'class' if ('td' == tagNode?.name || 'th'==tagNode?.name) { tagNode.name='td' String txt = tagNode.text tagNode.removeAllChildren() tagNode.insertChild(0, new ContentNode(txt)) } true } as TagNodeVisitor) serializer.writeToFile( result, tmpHtml.absolutePath, "utf-8" ) "pandoc -f html -t asciidoc -R -S --normalize -s $tmpHtml -o ${target}.adoc" .execute().waitFor() tmpHtml.delete() } }
  122. Android support • You can use Groovy to code Android

    apps! • use Groovy 2.4.0-beta-1+ • prefer @CompileStatic ! • Two great posts to get started: • http://melix.github.io/blog/2014/06/grooid.html • http://melix.github.io/blog/2014/06/grooid2.html 78
  123. What does NYT likes about Groovy on Android? • No

    Java 8, no lambda on Android… 80 Func0 func = new Func0<string>() { @Override public String call() { return "my content"; } }; Async.start(func);
  124. What does NYT likes about Groovy on Android? • No

    Java 8, no lambda on Android… 81 ! ! ! ! ! ! Async.start { "my content" }
  125. What does NYT likes about Groovy on Android? • No

    Java 8, no lambda on Android… 81 ! ! ! ! ! ! Async.start { "my content" } Good bye annonymous inner classes!
  126. What does NYT likes about Groovy on Android? ! •

    Groovy code more concise and more readable ! • but just as type-safe as needed!
 (with @TypeChecked) ! • but just as fast as needed!
 (with @CompileStatic) 82
  127. To know all about Android support 84 Groovy & Android,

    
 a winning pair? by Cédric Champeau Thu 12:45pm / Trinity 3
  128. © 2014 SpringOne 2GX. All rights reserved. Do not distribute

    without permission. A rich Groovy ecosystem
  129. Grails — web framework 86 @Grab("com.h2database:h2:1.3.173") import grails.persistence.* ! @Entity

    @Resource(uri='/books') class Book { String title } One class, one command, and you’ve got a full REST CRUD application!
  130. Ratpack — web framework 87 @GrabResolver("https://oss.jfrog.org/artifactory/repo") @Grab("org.ratpack-framework:ratpack-groovy:0.9.8") 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" } }
  131. Ratpack — web framework 87 @GrabResolver("https://oss.jfrog.org/artifactory/repo") @Grab("org.ratpack-framework:ratpack-groovy:0.9.8") 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" } } Lightweight Netty-based web application toolkit
  132. @ArtifactProviderFor(GriffonModel) class ConsoleModel { String scriptSource @Observable Object scriptResult @Observable

    boolean enabled = true } Griffon — rich desktop applications 88 Model @ArtifactProviderFor(GriffonController) class ConsoleController { def model ! @Inject Evaluator evaluator ! void executeScript() { model.enabled = false def result try { result = evaluator.evaluate(model.scriptSource) } finally { model.enabled = true model.scriptResult = result } } } Controller
  133. @ArtifactProviderFor(GriffonModel) class ConsoleModel { String scriptSource @Observable Object scriptResult @Observable

    boolean enabled = true } Griffon — rich desktop applications 88 Model @ArtifactProviderFor(GriffonController) class ConsoleController { def model ! @Inject Evaluator evaluator ! void executeScript() { model.enabled = false def result try { result = evaluator.evaluate(model.scriptSource) } finally { model.enabled = true model.scriptResult = result } } } Controller application(title: application.configuration['application.title'], pack: true, locationByPlatform: true, id: 'mainWindow', iconImage: imageIcon('/griffon-icon-48x48.png').image, iconImages: [imageIcon('/griffon-icon-48x48.png').image, imageIcon('/griffon-icon-32x32.png').image, imageIcon('/griffon-icon-16x16.png').image]) { panel(border: emptyBorder(6)) { borderLayout() scrollPane(constraints: CENTER) { textArea(text: bind(target: model, 'scriptSource'), enabled: bind { model.enabled }, columns: 40, rows: 10) } ! hbox(constraints: SOUTH) { button(executeScriptAction) hstrut(5) label('Result:') hstrut(5) textField(editable: false, text: bind { model.scriptResult }) } } } View
  134. Spock — unit testing & specification 89 @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 || 7 0 | 0 || 0 } }
  135. Spock — unit testing & specification 89 @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 || 7 0 | 0 || 0 } } Readable & concise expectations
  136. Spock — unit testing & specification 89 @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 || 7 0 | 0 || 0 } } Readable & concise expectations Awesome data-driven tests with a wiki-like notation
  137. Geb — browser automation 90 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" }
  138. Geb — browser automation 90 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" } Drive your browser
  139. Geb — browser automation 90 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" } Drive your browser JQuery-like selectors
  140. Geb — browser automation 90 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" } Drive your browser JQuery-like selectors Fill & submit forms
  141. Gradle — build automation 91 apply plugin: 'java' apply plugin:

    'eclipse' ! sourceCompatibility = 1.5 version = '1.0' jar { manifest { attributes 'Implementation-Title': 'Gradle Quickstart' } } ! repositories { mavenCentral() } ! dependencies { compile ‘commons-collections:commons-collections:3.2’ testCompile 'junit:junit:4.+' } ! uploadArchives { repositories { flatDir { dirs 'repos' } } }
  142. Gradle — build automation 91 apply plugin: 'java' apply plugin:

    'eclipse' ! sourceCompatibility = 1.5 version = '1.0' jar { manifest { attributes 'Implementation-Title': 'Gradle Quickstart' } } ! repositories { mavenCentral() } ! dependencies { compile ‘commons-collections:commons-collections:3.2’ testCompile 'junit:junit:4.+' } ! uploadArchives { repositories { flatDir { dirs 'repos' } } } Powerful and readable DSL for automating your builds and deployments
  143. GPars — concurrency / parallelism / async / … 92

    import static groovyx.gpars.actor.Actors.actor ! def decryptor = actor { loop { react { message -> if (message instanceof String) reply message.reverse() else stop() } } } ! def console = actor { decryptor.send 'lellarap si yvoorG' react { println 'Decrypted message: ${it}' decryptor.send false } } ! [decryptor, console]*.join()
  144. GPars — concurrency / parallelism / async / … 92

    import static groovyx.gpars.actor.Actors.actor ! def decryptor = actor { loop { react { message -> if (message instanceof String) reply message.reverse() else stop() } } } ! def console = actor { decryptor.send 'lellarap si yvoorG' react { println 'Decrypted message: ${it}' decryptor.send false } } ! [decryptor, console]*.join() Actors…
  145. GPars — concurrency / parallelism / async / … 92

    import static groovyx.gpars.actor.Actors.actor ! def decryptor = actor { loop { react { message -> if (message instanceof String) reply message.reverse() else stop() } } } ! def console = actor { decryptor.send 'lellarap si yvoorG' react { println 'Decrypted message: ${it}' decryptor.send false } } ! [decryptor, console]*.join() Actors… But also: Dataflow concurrency, CSP, agents, concurrent collection processing, fork / join, composable async functions, STM
  146. Groovy modules • Ant scripting • JMX • JSON •

    Servlet • Swing • SQL • Templating • Testing • XML 94
  147. Groovy modules • Ant scripting • JMX • JSON •

    Servlet • Swing • SQL • Templating • Testing • XML 95
  148. Ant + XML 96 def writer = new StringWriter() def

    mkp = new MarkupBuilder(writer) ! mkp.html { head { title 'Build notification' } body { p 'Your build was successful' } } ! new AntBuilder().mail(mailhost: 'localhost', messagemimetype: 'text/html', subject: 'Build successful') { ! from address: '[email protected]' to address: '[email protected]' message writer attchments { fileset(dir: 'dist') { include name: '**/logs*.txt' } } }
  149. Ant + XML 96 def writer = new StringWriter() def

    mkp = new MarkupBuilder(writer) ! mkp.html { head { title 'Build notification' } body { p 'Your build was successful' } } ! new AntBuilder().mail(mailhost: 'localhost', messagemimetype: 'text/html', subject: 'Build successful') { ! from address: '[email protected]' to address: '[email protected]' message writer attchments { fileset(dir: 'dist') { include name: '**/logs*.txt' } } } Generate some HTML with the XML support
  150. Ant + XML 96 def writer = new StringWriter() def

    mkp = new MarkupBuilder(writer) ! mkp.html { head { title 'Build notification' } body { p 'Your build was successful' } } ! new AntBuilder().mail(mailhost: 'localhost', messagemimetype: 'text/html', subject: 'Build successful') { ! from address: '[email protected]' to address: '[email protected]' message writer attchments { fileset(dir: 'dist') { include name: '**/logs*.txt' } } } Generate some HTML with the XML support Use the Ant builder and the mail task
  151. Ant + XML 96 def writer = new StringWriter() def

    mkp = new MarkupBuilder(writer) ! mkp.html { head { title 'Build notification' } body { p 'Your build was successful' } } ! new AntBuilder().mail(mailhost: 'localhost', messagemimetype: 'text/html', subject: 'Build successful') { ! from address: '[email protected]' to address: '[email protected]' message writer attchments { fileset(dir: 'dist') { include name: '**/logs*.txt' } } } Generate some HTML with the XML support Use the Ant builder and the mail task Use the Ant’s fileset
  152. JSON support — creating JSON 97 import groovy.json.* ! def

    json = new JsonBuilder() json.person { name 'Guillaume' age 37 daughters 'Marion', 'Erine' address { street '1 Main Street' zip 75001 city 'Paris' } }
  153. JSON support — creating JSON 97 import groovy.json.* ! def

    json = new JsonBuilder() json.person { name 'Guillaume' age 37 daughters 'Marion', 'Erine' address { street '1 Main Street' zip 75001 city 'Paris' } } { "person": { "name": "Guillaume", "age": 37, "daughters": [ "Marion", "Erine" ], "address": { "street": "1 Main Street", "zip": 75001, "city": "Paris" } } }
  154. JSON support — parsing JSON 98 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'
  155. JSON support — parsing JSON 98 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' The power of a dynamic language!
  156. Template module — new markup template engine • Based on

    the principles of Groovy’s « builders » • and particularly the MarkupBuilder class
 for generating arbitrary XML / HTML payloads ! • Compiled statically for fast template rendering ! • Internationalization aware • provide the desired Locale in the configuration object • usual suffix notation template_fr_FR.tpl ! • Custom base template class • ability to provide reusable methods across your templates 99
  157. Template module — new markup template engine • Based on

    the principles of Groovy’s « builders » • and particularly the MarkupBuilder class
 for generating arbitrary XML / HTML payloads ! • Compiled statically for fast template rendering ! • Internationalization aware • provide the desired Locale in the configuration object • usual suffix notation template_fr_FR.tpl ! • Custom base template class • ability to provide reusable methods across your templates 99 Spring Boot approved
  158. Markup template engine — the idea 100 cars { cars.each

    { car(make: it.make, name: it.name) } }
  159. Markup template engine — the idea 100 cars { cars.each

    { car(make: it.make, name: it.name) } } Your template
  160. Markup template engine — the idea 100 cars { cars.each

    { car(make: it.make, name: it.name) } } model = [cars: [ new Car(make: 'Peugeot', name: '508'), new Car(make: 'Toyota', name: 'Prius’) ]]
  161. Markup template engine — the idea 100 cars { cars.each

    { car(make: it.make, name: it.name) } } model = [cars: [ new Car(make: 'Peugeot', name: '508'), new Car(make: 'Toyota', name: 'Prius’) ]] Feed a model into your template
  162. Markup template engine — the idea 100 cars { cars.each

    { car(make: it.make, name: it.name) } } model = [cars: [ new Car(make: 'Peugeot', name: '508'), new Car(make: 'Toyota', name: 'Prius’) ]] <cars> <car make='Peugeot' name='508'/> <car make='Toyota' name='Prius'/> </cars>
  163. Markup template engine — the idea 100 cars { cars.each

    { car(make: it.make, name: it.name) } } model = [cars: [ new Car(make: 'Peugeot', name: '508'), new Car(make: 'Toyota', name: 'Prius’) ]] <cars> <car make='Peugeot' name='508'/> <car make='Toyota' name='Prius'/> </cars> Generate the XML output
  164. Markup template engine — the idea 100 cars { cars.each

    { car(make: it.make, name: it.name) } } model = [cars: [ new Car(make: 'Peugeot', name: '508'), new Car(make: 'Toyota', name: 'Prius’) ]] <cars> <car make='Peugeot' name='508'/> <car make='Toyota' name='Prius'/> </cars>
  165. Markup template engine — in action 101 import  groovy.text.markup.*  

    ! def  config  =  new  TemplateConfiguration()   def  engine  =  new  MarkupTemplateEngine(config)   def  tmpl  =  engine.createTemplate('''          p("Hello  ${model.name}")   ''')   def  model  =  [name:  'World']   System.out  <<  tmpl.make(model)
  166. Longer answer: Yes, because… • You can benefit from Java

    8 in Groovy 105 Synergy
 the whole is 
 greater than the sum of the parts
  167. Longer answer: Yes, because… • Groovy goes beyond what Java

    8 offers 106 Beyond
 Groovy always tries to add something to the table
  168. Questions still open: Syntax support for… • lambdas • not

    necessarily,
 (confusing / duplication) ! • method references • enhance method closures • or replace them with method references ! • interface default methods • yes: traits methods aren’t default methods • repeated annotations • yes: not urgent • but less boilerplate is good ! • annotations on type • yes: opens up new possibilities for targets of local AST transformation ! • interface static methods • yes: for Java compatibility 107
  169. Further possible API enhancements • Make Optional Groovy-friendly • with

    regards to Groovy Truth • accessing the wrapped value ! • More Groovy methods for… • NIO • Streams • Date / time ! • Operator overloading for Date / time • for arithmetics on instants and durations 108
  170. Further possible API enhancements • Make Optional Groovy-friendly • with

    regards to Groovy Truth • accessing the wrapped value ! • More Groovy methods for… • NIO • Streams • Date / time ! • Operator overloading for Date / time • for arithmetics on instants and durations 108 Community feedback & contributions welcome!
  171. More Java 8 coverage 109 Java 8 language capabilities by

    V. Subramaniam Thu 10:30pm / D.Ball.G
  172. More Java 8 coverage 110 How to get Groovy with

    Java 8 by Peter Ledbrook Thu 10:30pm / Trinity 3
  173. Java 8 in Action ! • By Urma, Fusco and

    Mycrof • Plublished by Manning • http://www.manning.com/urma/ 111
  174. Image credits • Question mark • http://www.mynamesnotmommy.com/wp-content/uploads/2013/05/question-mark.png • Get out!

    • http://static.comicvine.com/uploads/original/11/117995/3772037-0075368930-27616.jpg • Yes • http://soloprpro.com/wp-content/uploads/2013/08/yes.jpg • Synergy • http://www.wildblueberries.com/wp-content/uploads/blogger/_YhH8fDK5-kU/S_5kYWwgAbI/AAAAAAAAAKU/JQ_-ISfT9KY/ s1600/teamwork.jpg • Buzz Lightyear • http://www.disneypictures.net/data/media/202/Buzz_Lightyear_hd.jpg • Start wars spaceship • http://swc.fs2downloads.com/media/screenshots/Support_Trans/Shuttle/lambda003.jpg • Lambda character • http://lambda.ninjackaton.ninja-squad.com/images/lambda.png • Man clock • http://3.bp.blogspot.com/-7hLQ9tnmA84/TuTIoLRLMTI/AAAAAAAABWM/g7ahyLCRjJQ/s1600/Harold+Lloyd+Safety+Last.jpg • Stream • http://wallpaperswide.com/forest_stream-wallpapers.html • Nashorn (rhino) • http://2010sdafrika.files.wordpress.com/2012/07/hi_257587-nashorn-c-naturepl-com-mark-carwardine-wwf-canon.jpg 114
  175. Image credits • Brain • http://www.semel.ucla.edu/sites/all/files/users/user-412/dreamstime_xxl_17754591%20(2).jpg • Many thanks •

    http://www.trys.ie/wp-content/uploads/2013/06/many-thanks.jpg • Swing • http://makemesomethingspecial.co.uk/wp-content/uploads/2012/10/Solid-Oak-Handmade-Spliced-Swing-With-Personalised- Engraving-10867.jpg • Disclaimer • http://3.bp.blogspot.com/-RGnBpjXTCQA/Tj2h_JsLigI/AAAAAAAABbg/AB5ZZYzuE5w/s1600/disclaimer.jpg • Hammer / justice / truth • http://www.bombayrealty.in/images/disclaimer.jpg • Law • http://www.permanentmakeupbymv.com/wp-content/uploads/2014/07/law-2.jpg • Glasses / readable • http://a.fastcompany.net/multisite_files/coexist/imagecache/1280/poster/2013/02/1681393-poster-1280-responsive-eye-chart.jpg • We need you • http://www.bostonbreakerssoccer.com/imgs/ABOUT/volopps/we%20need%20you.JPG 115
  176. Image credits • Jackson Pollock • http://www.ibiblio.org/wm/paint/auth/pollock/pollock.number-8.jpg • Turtles •

    http://33.media.tumblr.com/77095e3a37acb2272133c405b1f7ba54/tumblr_myfydjNgXJ1s20kxvo1_1280.jpg • Annotations • http://3.bp.blogspot.com/-f94n9BHko_s/T35ELs7nYvI/AAAAAAAAAKg/qe06LRPH9U4/s1600/IMG_1721.JPG • Builders • http://detroittraining.com/wp-content/uploads/Construction-Women2.jpg • Sandwich ingredients • http://www.fromages-de-terroirs.com/IMG/MEULE_DETOUREE_72_DPI_-2.jpg • http://eboutique-hebergement.orange-business.com/WebRoot/Orange/Shops/Madeinalsace/4B02/6320/943E/DB4F/DDBF/ 0A0A/33E8/4BC8/iStock_000008775559XSmall.jpg • http://www.cmonprimeur.fr/images/25834-vegetable-pictures%5B1%5D.jpg • http://1.bp.blogspot.com/-27UoqYeYtY4/T4PAr4zGSnI/AAAAAAAAA3s/lleE-NOh0LE/s1600/IMG_1528.JPG • http://www.growingpatch.com/wp-content/uploads/2014/05/tomato-9.jpg • http://vitaminsandhealthsupplements.com/wp-content/uploads/2013/08/fresh-sliced-tomato.jpg • http://2.bp.blogspot.com/-3pPmRhBHv9s/T3D-EtLlN1I/AAAAAAAAAgg/Y5oTM84nGb8/s1600/cochon+2.jpg • http://www.salamarket.fr/wp-content/uploads/steak-hach%C3%A9.jpg • http://www.quinzanisbakery.com/images/bread-vienna.jpg • http://www.saucissonvaudois.qc.ca/images/produits/Jambon_blanc.jpg • http://www.audion.com/system/public/categories/125/images/bread-sandwich.jpg 116