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?

137d3908243acfc30e126615d59d4e6d?s=128

Guillaume Laforge

September 09, 2014
Tweet

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. Stay up-to-date Groovy Weekly Newsletter (Every Tuesday) http://beta.groovy-lang.org/groovy-weekly.html 2

  3. Stay up-to-date Google+ Groovy Page https://google.com/+groovy 3

  4. Stay up-to-date Google+ Groovy Community http://bit.ly/g-community 4

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

    without permission. Goal of this talk
  6. A recurring question… Do we still need Groovy now that

    we have Java 8? 6
  7. To those who said no… 7

  8. 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
  9. What about redundancy? ! ! ! ! ! • Closures

    • Traits • Truth & null handling • Functional with collections • Method closures ! ! ! ! ! ! ! • Lambdas • Default methods • Optional • Stream API • Method references 9
  10. © 2014 SpringOne 2GX. All rights reserved. Do not distribute

    without permission. Agenda
  11. 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
  12. © 2014 SpringOne 2GX. All rights reserved. Do not distribute

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

    the details :-)
  15. This is not a Java 8 crash courses with all

    the details :-) We want to understand the implications with regards to Groovy
  16. 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
  17. 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
  18. © 2014 SpringOne 2GX. All rights reserved. Do not distribute

    without permission. Support the new Java 8 language features New Java 8 syntax
  19. 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
  20. 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
  21. None
  22. None
  23. None
  24. Lambda expressions 19 double highestScore = students .filter(Student s ->

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

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

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

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

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

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

    == 2011 } .collect { it.score} .max()
  31. Lambda expressions 21 double highestScore = students .findAll { it.gradYear

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

    == 2011 } .collect { it.score} .max() Drawback: intermediary data structures Unless you use iterator variants
  33. Lambda expressions 21 double highestScore = students .findAll { it.gradYear

    == 2011 } .collect { it.score} .max()
  34. 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) }
  35. 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
  36. 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
  37. 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
  38. 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) }
  39. The various lambda syntaxes 23 name -> name.length() ! (left,

    right) -> left + right ! (left, sep, right) -> { System.out.println(left + sep + right) }
  40. 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
  41. 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));
  42. 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 }
  43. 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
  44. 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)
  45. 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 }
  46. Beyond: Closure default parameters 25 def mult = { int

    a, int b = 10 -> a * b } ! assert mult(2, 3) == 6 assert mult(5) == 50
  47. 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
  48. Beyond: Duck typing polymorphism 26 def adder = { a,

    b -> a + b } ! assert adder(100, 200) == 300 assert adder('X', 'Y') == 'XY'
  49. 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
  50. Builders

  51. Builders What would Java lambda builders look like?

  52. Builders What would Java lambda builders look like? Aren’t Groovy

    builders more powerful?
  53. 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"); }); });
  54. 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
  55. 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
  56. 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
  57. 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" } } }
  58. 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" } } }
  59. None
  60. Aren’t Groovy builders more readable and lean?

  61. To those who said no… 31

  62. Memoization

  63. Closure and method memoization 33 def fib2 = { long

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

    n -> if (n < 2) 1 else call(n - 1) + call(n - 2) }.memoize() Closures: memoize()
  65. 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()
  66. 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
  67. None
  68. Tail recursion

  69. Closure and method tail recursion 35 def fact = {

    n, accu = 1G -> if (n < 2) accu else fact.trampoline(n - 1, n * accu) }.trampoline()
  70. 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()
  71. 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()
  72. 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
  73. Method references 36 button.setOnAction(event -> System.out.println(event));

  74. Method references 36 button.setOnAction(event -> System.out.println(event)); button.setOnAction(System.out::println);

  75. Method references — 3 main cases 37 instance::instanceMethod ! SomeClass::staticMethod

    ! SomeClass::instanceMethod
  76. Method references — 3 main cases 37 instance::instanceMethod ! SomeClass::staticMethod

    ! SomeClass::instanceMethod Not covered by Groovy method closures yet!
  77. Groovy’s method closure 38 instance.&instanceMethod ! SomeClass.&staticMethod ! SomeClass.&instanceMethod

  78. Groovy’s method closure 38 instance.&instanceMethod ! SomeClass.&staticMethod ! SomeClass.&instanceMethod Choices:

    Adopt :: Deprecate .& Enhance .&
  79. 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> { ... } } }
  80. Default methods in interfaces • Define default behavior • possibly

    to enrich existing interfaces 40 public interface Stream<T> { // ... default Builder<T> add(T t) { ... } }
  81. 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
  82. Simple trait 42 trait FlyingAbility { String fly() { "I'm

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

    ! class Bird implements Named {} def b = new Bird(name: 'Colibri') ! assert b.name == 'Colibri'
  84. 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'
  85. To know all about traits! 45 Rethinking API 
 design

    with traits by Cédric Champeau Tue 2:30pm / Trinity 3
  86. Annotations on types Repeating annotations

  87. Repeating annotations 47 @Schedule(dayOfMonth = "last") @Schedule(dayOfWeek = "Fri", hour

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

    = 23) public void doPeriodicCleanup() { ... } @Schedule annotation repeated twice
  89. 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; }
  90. 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
  91. 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(); }
  92. 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
  93. 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(); }
  94. 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
  95. 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(); }
  96. 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> {...}
  97. 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
  98. 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?
  99. 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> {...}
  100. Groovy compile-time meta-annotations 49 @Service @Transactional class MyTransactionalService {}

  101. Groovy compile-time meta-annotations 49 @Service @Transactional class MyTransactionalService {} import

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

    public @interface TransactionalService {} ! @TransactionalService class MyTransactionalService {}
  103. 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 »
  104. Groovy compile-time meta-annotations 49 import groovy.transform.AnnotationCollector ! @Service @Transactional @AnnotationCollector

    public @interface TransactionalService {} ! @TransactionalService class MyTransactionalService {}
  105. © 2014 SpringOne 2GX. All rights reserved. Do not distribute

    without permission. New Java 8 APIs
  106. Date and Time API

  107. 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));
  108. 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?
  109. 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));
  110. 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 }
  111. 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
  112. 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 }
  113. Stream API

  114. map map map map map reduce reduce reduce reduce reduce

  115. map map map map map reduce reduce reduce reduce reduce

    Map / filter / reduce explained to your 6 year old
  116. map map map map map reduce reduce reduce reduce reduce

  117. Stream 56 persons.stream() .filter( p -> p.getAge() < 18 )

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

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

    18 } .map { it.name.toUpperCase() } .sorted() .collect(Collectors.joining(", "))
  120. 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
  121. Groovy using streams too! 58 persons.stream() .filter { it.age <

    18 } .map { it.name.toUpperCase() } .sorted() .collect(Collectors.joining(", "))
  122. 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"); }
  123. 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
  124. 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"); }
  125. 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
  126. 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"); }
  127. None
  128. You know you can customize the truth in Groovy?

  129. You know you can customize the truth in Groovy? Just

    implement a custom asBoolean() method!
  130. The law of Groovy Truth 61

  131. The law of Groovy Truth 61 Everything that’s null, empty,

    zero-sized, equal to zero is false
  132. The law of Groovy Truth 61 Everything that’s null, empty,

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

  134. 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()
  135. 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!
  136. 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!
  137. 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
  138. 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
  139. 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
  140. 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()
  141. Nashorn

  142. Nashorn Speak German?

  143. Call JavaScript from Groovy with JSR-223 65 def manager =

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

    new ScriptEngineManager() def engine = manager.getEngineByName("nashorn") ! assert engine.eval("{} + []") == 0 Sane JavaScript logic :-)
  145. GrooScript — http://grooscript.org/ 66 JS

  146. GrooScript — http://grooscript.org/ 66 JS Gradle & Grails plugins available

    Examples available with Ratpack, Node.JS…
  147. Turtles all the way down!

  148. Turtles all the way down! Full Groovy!

  149. Turtles all the way down! Full Groovy! Back to front

  150. Turtles all the way down! Full Groovy! Back to front

    Front to back
  151. None
  152. Jackson Pollock

  153. JavaFx… ! ! …the new Swing

  154. 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") } }
  155. 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!
  156. © 2014 SpringOne 2GX. All rights reserved. Do not distribute

    without permission. Beyond Java
  157. © 2014 SpringOne 2GX. All rights reserved. Do not distribute

    without permission. Easy scripting
  158. 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
  159. 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'
  160. 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
  161. 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'
  162. 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
  163. 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'
  164. 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
  165. 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'
  166. 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" )
  167. 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
  168. 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" )
  169. 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
  170. 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" )
  171. 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() } }
  172. 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
  173. 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() } }
  174. 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
  175. 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() } }
  176. © 2014 SpringOne 2GX. All rights reserved. Do not distribute

    without permission. Android
  177. 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
  178. New York Times — Getting Groovy with Android 79

  179. New York Times — Getting Groovy with Android 79 http://bit.ly/nyt-groovy

  180. 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);
  181. What does NYT likes about Groovy on Android? • No

    Java 8, no lambda on Android… 81 ! ! ! ! ! ! Async.start { "my content" }
  182. 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!
  183. 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
  184. Android support 83

  185. Android support 83

  186. Android support 83 Source code available: https://github.com/melix/gr8confagenda

  187. To know all about Android support 84 Groovy & Android,

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

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

    @Resource(uri='/books') class Book { String title }
  190. 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!
  191. 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" } }
  192. 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
  193. @ArtifactProviderFor(GriffonModel) class ConsoleModel { String scriptSource @Observable Object scriptResult @Observable

    boolean enabled = true } Griffon — rich desktop applications 88
  194. @ArtifactProviderFor(GriffonModel) class ConsoleModel { String scriptSource @Observable Object scriptResult @Observable

    boolean enabled = true } Griffon — rich desktop applications 88 Model
  195. @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
  196. @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
  197. 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 } }
  198. 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
  199. 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
  200. 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" }
  201. 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
  202. 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
  203. 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
  204. 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' } } }
  205. 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
  206. 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()
  207. 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…
  208. 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
  209. © 2014 SpringOne 2GX. All rights reserved. Do not distribute

    without permission. Groovy modules
  210. Groovy modules • Ant scripting • JMX • JSON •

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

    Servlet • Swing • SQL • Templating • Testing • XML 95
  212. 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: 'ci@mycomp.com' to address: 'me@mycomp.com' message writer attchments { fileset(dir: 'dist') { include name: '**/logs*.txt' } } }
  213. 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: 'ci@mycomp.com' to address: 'me@mycomp.com' message writer attchments { fileset(dir: 'dist') { include name: '**/logs*.txt' } } } Generate some HTML with the XML support
  214. 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: 'ci@mycomp.com' to address: 'me@mycomp.com' 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
  215. 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: 'ci@mycomp.com' to address: 'me@mycomp.com' 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
  216. 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' } }
  217. 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" } } }
  218. 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'
  219. 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!
  220. 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
  221. 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
  222. Markup template engine — the idea 100 cars { cars.each

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

    { car(make: it.make, name: it.name) } } Your template
  224. 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’) ]]
  225. 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
  226. 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>
  227. 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
  228. 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>
  229. 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)
  230. © 2014 SpringOne 2GX. All rights reserved. Do not distribute

    without permission. Summary
  231. Back to our original question… 103 Do we still need

    Groovy now that we have Java 8?
  232. None
  233. Longer answer: Yes, because… • You can benefit from Java

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

    8 offers 106 Beyond
 Groovy always tries to add something to the table
  235. 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
  236. 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
  237. 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!
  238. More Java 8 coverage 109 Java 8 language capabilities by

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

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

    Mycrof • Plublished by Manning • http://www.manning.com/urma/ 111
  241. © 2014 SpringOne 2GX. All rights reserved. Do not distribute

    without permission. Q & A
  242. None
  243. 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
  244. 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
  245. 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