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

Groovy in the light of Java 8 -- JavaOne 2014

Groovy in the light of Java 8 -- JavaOne 2014

With Java 8 out, what is the impact of Groovy? Is it still relevant? How can developers can benefit from Java 8 with Groovy?

137d3908243acfc30e126615d59d4e6d?s=128

Guillaume Laforge

October 02, 2014
Tweet

Transcript

  1. 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. @glaforge — Groovy in the light of Java 8 Goal

    of this talk
  4. A recurring question… Do we still need Groovy now that

    we have Java 8? 4
  5. To those who said no… 5

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

    • Traits • Truth & null handling • Functional with collections • Method closures ! ! ! ! ! ! • Lambdas • Default methods • Optional • Stream API • Method references 7
  8. @glaforge — Groovy in the light of Java 8 What’s

    new in Java 8?
  9. None
  10. This is not a Java 8 crash courses with all

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

    the details :-) We want to understand the implications with regards to Groovy
  12. 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 10
  13. 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 10 Great concise resource with the whole list: http://bit.ly/new-in-java-8
  14. @glaforge — Groovy in the light of Java 8 Support

    the new Java 8 language features New Java 8 syntax
  15. 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 12
  16. 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 13
  17. None
  18. None
  19. None
  20. Lambda expressions 15 double highestScore = students .filter(Student s ->

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

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

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

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

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

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

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

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

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

    == 2011 } .collect { it.score} .max()
  30. The various lambda syntaxes 18 String name -> name.length() !

    (int left, int right) -> left + right ! (String left, String sep, String right) -> { System.out.println(left + sep + right) }
  31. The various lambda syntaxes 18 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
  32. The various lambda syntaxes 18 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
  33. The various lambda syntaxes 18 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
  34. The various lambda syntaxes 18 String name -> name.length() !

    (int left, int right) -> left + right ! (String left, String sep, String right) -> { System.out.println(left + sep + right) } Clever type inference can help get rid of parameter type declarations
  35. Closures vs lambdas 19 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));
  36. Closures vs lambdas 19 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 }
  37. Closures vs lambdas 19 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
  38. Closures vs lambdas 19 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)
  39. Closures vs lambdas 19 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 }
  40. Beyond: Closure default parameters 20 def mult = { int

    a, int b = 10 -> a * b } ! assert mult(2, 3) == 6 assert mult(5) == 50
  41. Beyond: Closure default parameters 20 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
  42. Beyond: Duck typing polymorphism 21 def adder = { a,

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

    b -> a + b } ! assert adder(100, 200) == 300 assert adder('X', 'Y') == 'XY' Works both for numbers and for strings
  44. Builders

  45. Builders What would Java lambda builders look like?

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

    builders more powerful?
  47. Lambda-based builders uglier and less powerful 23 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"); }); });
  48. Lambda-based builders uglier and less powerful 23 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
  49. Lambda-based builders uglier and less powerful 23 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
  50. Lambda-based builders uglier and less powerful 23 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
  51. Neater Groovy builder! 24 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" } } }
  52. Neater Groovy builder! 24 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" } } }
  53. None
  54. Aren’t Groovy builders more readable and lean?

  55. To those who said no… 26

  56. Memoization

  57. Closure and method memoization 28 def fib2 = { long

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

    n -> if (n < 2) 1 else call(n - 1) + call(n - 2) }.memoize() Closures: memoize()
  59. Closure and method memoization 28 @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()
  60. Closure and method memoization 28 @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
  61. None
  62. Tail recursion

  63. Closure and method tail recursion 30 def fact = {

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

    n, accu = 1G -> if (n < 2) accu else fact.trampoline(n - 1, n * accu) }.trampoline() Closures: Tail recursion with trampoline()
  65. Closure and method tail recursion 30 @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()
  66. Closure and method tail recursion 30 @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
  67. Method references 31 button.setOnAction(event -> System.out.println(event));

  68. Method references 31 button.setOnAction(event -> System.out.println(event)); button.setOnAction(System.out::println);

  69. Method references — 3 main cases • Three main cases:

    32 instance::instanceMethod ! SomeClass::staticMethod ! SomeClass::instanceMethod
  70. Method references — 3 main cases • Three main cases:

    32 instance::instanceMethod ! SomeClass::staticMethod ! SomeClass::instanceMethod Not covered by Groovy method closures yet!
  71. Groovy’s method closure 33 instance.&instanceMethod ! SomeClass.&staticMethod ! SomeClass.&instanceMethod

  72. Groovy’s method closure 33 instance.&instanceMethod ! SomeClass.&staticMethod ! SomeClass.&instanceMethod Choices:

    Adopt :: Deprecate .& Enhance .&
  73. Static methods in interfaces • You can put static utility

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

    to enrich existing interfaces 35 public interface Stream<T> { // ... default Builder<T> add(T t) { ... } }
  75. 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 36
  76. Simple trait 37 trait FlyingAbility { String fly() { "I'm

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

    ! class Bird implements Named {} def b = new Bird(name: 'Colibri') ! assert b.name == 'Colibri'
  78. Multiple inheritance 39 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'
  79. Annotations on types Repeating annotations

  80. Repeating annotations 41 @Schedule(dayOfMonth = "last") @Schedule(dayOfWeek = "Fri", hour

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

    = 23) public void doPeriodicCleanup() { ... } @Schedule annotation repeated twice
  82. Repeating annotations 41 @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; }
  83. Repeating annotations 41 @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
  84. Repeating annotations 41 @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(); }
  85. Repeating annotations 41 @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
  86. Repeating annotations 41 @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(); }
  87. Repeating annotations 41 @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
  88. Repeating annotations 41 @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(); }
  89. Annotations on types • Everywhere you can put a type,

    you can put an annotation 42 @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> {...}
  90. Annotations on types • Everywhere you can put a type,

    you can put an annotation 42 @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
  91. Annotations on types • Everywhere you can put a type,

    you can put an annotation 42 @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?
  92. Annotations on types • Everywhere you can put a type,

    you can put an annotation 42 @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> {...}
  93. Groovy compile-time meta-annotations 43 @Service @Transactional class MyTransactionalService {}

  94. Groovy compile-time meta-annotations 43 @Service @Transactional class MyTransactionalService {} import

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

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

    public @interface TransactionalService {} ! @TransactionalService class MyTransactionalService {}
  98. @glaforge — Groovy in the light of Java 8 New

    Java 8 APIs
  99. Date and Time API

  100. Date / Time API 46 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));
  101. Date / Time API 46 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?
  102. Date / Time API 46 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));
  103. Groovy’s date / time handling 47 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 }
  104. Groovy’s date / time handling 47 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
  105. Groovy’s date / time handling 47 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 }
  106. Stream API

  107. map map map map map reduce reduce reduce reduce reduce

  108. map map map map map reduce reduce reduce reduce reduce

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

  110. Stream 50 persons.stream() .filter( p -> p.getAge() < 18 )

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

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

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

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

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

    maybeName.orElse("unknown") ! if (maybeName.ifPresent()) { System.out.println(maybeName.get()); } else { System.out.println("unknown"); }
  116. Optional 53 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
  117. Optional 53 Optional<String> maybeName = Optional.of("Guillaume"); ! String result =

    maybeName.orElse("unknown") ! if (maybeName.ifPresent()) { System.out.println(maybeName.get()); } else { System.out.println("unknown"); }
  118. Optional 53 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
  119. Optional 53 Optional<String> maybeName = Optional.of("Guillaume"); ! String result =

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

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

    implement a custom asBoolean() method!
  123. The law of Groovy Truth 55

  124. The law of Groovy Truth 55 Everything that’s null, empty,

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

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

  127. Groovy truth, null handling, Elvis with Optional 57 def maybeName

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

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

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

    = Optional.of("Guillaume") ! def result = maybeName ?: "unknown" ! if (maybeName) { println maybeName.get() } else { println "unknown" } ! maybeName?.toUpperCase() Safe navigation
  131. Groovy truth, null handling, Elvis with Optional 57 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
  132. Groovy truth, null handling, Elvis with Optional 57 def maybeName

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

    = Optional.of("Guillaume") ! def result = maybeName ?: "unknown" ! if (maybeName) { println maybeName.get() } else { println "unknown" } ! maybeName?.toUpperCase()
  134. Nashorn

  135. Nashorn Speak German?

  136. Call JavaScript from Groovy with JSR-223 59 def manager =

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

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

  139. GrooScript — http://grooscript.org/ 60 JS Gradle & Grails plugins available

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

  141. Turtles all the way down! Full Groovy!

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

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

    Front to back
  144. JavaFx… ! ! …the new Swing

  145. GroovyFX 63 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") } }
  146. GroovyFX 63 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!
  147. @glaforge — Groovy in the light of Java 8 Beyond

    Java
  148. @glaforge — Groovy in the light of Java 8 Android

  149. 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 66
  150. New York Times — Getting Groovy with Android 67

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

  152. What does NYT likes about Groovy on Android? • No

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

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

    Java 8, no lambda on Android… 69 ! ! ! ! ! ! Async.start { "my content" } Good bye annonymous inner classes!
  155. 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) 70
  156. Android support 71

  157. Android support 71 Source code available: https://github.com/melix/gr8confagenda

  158. @glaforge — Groovy in the light of Java 8 A

    rich Groovy ecosystem
  159. Grails — web framework 73 @Grab("com.h2database:h2:1.3.173") import grails.persistence.* ! @Entity

    @Resource(uri='/books') class Book { String title }
  160. Grails — web framework 73 @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!
  161. Ratpack — web framework 74 @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" } }
  162. Ratpack — web framework 74 @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
  163. @ArtifactProviderFor(GriffonModel) class ConsoleModel { String scriptSource @Observable Object scriptResult @Observable

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

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

    boolean enabled = true } Griffon — rich desktop applications 75 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
  166. @ArtifactProviderFor(GriffonModel) class ConsoleModel { String scriptSource @Observable Object scriptResult @Observable

    boolean enabled = true } Griffon — rich desktop applications 75 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
  167. Spock — unit testing & specification 76 @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 } }
  168. Spock — unit testing & specification 76 @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
  169. Spock — unit testing & specification 76 @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
  170. Geb — browser automation 77 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" }
  171. Geb — browser automation 77 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
  172. Geb — browser automation 77 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
  173. Geb — browser automation 77 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
  174. Gradle — build automation 78 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' } } }
  175. Gradle — build automation 78 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
  176. GPars — concurrency / parallelism / async / … 79

    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()
  177. GPars — concurrency / parallelism / async / … 79

    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…
  178. GPars — concurrency / parallelism / async / … 79

    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
  179. @glaforge — Groovy in the light of Java 8 Groovy

    modules
  180. Groovy modules • Ant scripting • JMX • JSON •

    Servlet • Swing • SQL • Templating • Testing • XML 81
  181. Groovy modules • Ant scripting • JMX • JSON •

    Servlet • Swing • SQL • Templating • Testing • XML 82
  182. Ant + XML 83 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' } } }
  183. Ant + XML 83 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
  184. Ant + XML 83 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
  185. Ant + XML 83 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
  186. JSON support — creating JSON 84 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' } }
  187. JSON support — creating JSON 84 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" } } }
  188. JSON support — parsing JSON 85 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'
  189. JSON support — parsing JSON 85 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!
  190. 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 86
  191. 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 86 Spring Boot approved
  192. Markup template engine — the idea 87 cars { cars.each

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

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

    { car(make: it.make, name: it.name) } } model = [cars: [ new Car(make: 'Peugeot', name: '508'), new Car(make: 'Toyota', name: 'Prius’) ]]
  195. Markup template engine — the idea 87 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
  196. Markup template engine — the idea 87 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>
  197. Markup template engine — the idea 87 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
  198. Markup template engine — the idea 87 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>
  199. Markup template engine — in action 88 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)
  200. @glaforge — Groovy in the light of Java 8 Summary

  201. Back to our original question… Do we still need Groovy

    now that we have Java 8? 90
  202. None
  203. Longer answer: Yes, because… • You can benefit from Java

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

    8 offers 93 Beyond
 Groovy always tries to add something to the table
  205. 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 94
  206. 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 95
  207. 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 95 Community feedback & contributions welcome!
  208. @glaforge — Groovy in the light of Java 8 Q

    & A
  209. None
  210. 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 98
  211. 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 • Golden Gate • http://www.airpano.com/files/San-Francisco-Golden-Gate-USA/sf_golden_gate.jpg 99
  212. Image credits • 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 100