What makes
Groovy groovy?
Guillaume Laforge
@glaforge
© 2013 Guillaume Laforge. All rights reserved. Do not distribute wit...
Guillaume
Laforge
Groovy project lead
at
.
@glaforge
http://glaforge.appspot.com
Les Cast
Codeurs
1
t
r
a
P

The Groovy
vision
Simplify the life of
(Java) developers
Great for
scripting
Great for
scripting

Fit for DomainSpecific Languages
Great for
scripting

Fit for DomainSpecific Languages

Most seamless integration &
interoperability wih java!
It’s so easy
to learn!
Groovy as a
Java superset

It’s so easy
to learn!
class!MathSpec!extends!Specification!{
!!!!def!"maximum!of!two!numbers"()!{
******expect:
!!!!!!!!Math.max(a,!b)!==!c
****...
As safe and fast as Java with
static type checking & compilation
As safe and fast as Java with
static type checking & compilation
new!MarkupBuilder().html!{
!!!!head!{
!!!!!!!!title!"The!Script!Bowl"
!!!!}
!!!!body!{
!!!!!!!!div(class:!"banner")!{
!!!!...
move!forward!at!3.km/h
Expressive,
Concise,
Readable
Speaking of conciseness...
A full Spring app in the span of a tweet!
@RestController
class!App!{
!!!!@RequestMapping("/")
...
• SmartThings
• Carriots

Services

• European Patent
Office
• Amadeus
IoT

Financial

• Crédit Suisse
• JPMorgan
• Fanny M...
2
t
r
a
P

Cool Groovy
gems
Most Java code is also
valid Groovy code!
Most Java code is also
valid Groovy code!

Any Java developer is a
Groovy developer!
Flat learning
curve
Flat learning
curve

Easy to learn
Scripts versus Classes
public!class!Main!{
!!!!public!static!void!main(String[]!args)!{
!!!!!!!!System.out.println("Hello"...
Scripts versus Classes
public!class!Main!{
!!!!public!static!void!main(String[]!args)!{
!!!!!!!!System.out.println("Hello"...
Optional
Semicolons

Optional
Semicolons
Parentheses

Optional
Semicolons
Parentheses

Optional
return keyword
Semicolons
Parentheses

Optional
return keyword

public keyword
Semicolons
Parentheses

Optional
Typing!
return keyword

public keyword
Optional...
public!class!Greeter!{
!!!!private!String!owner;
!!!!public!String!getOwner()!{
!!!!!!!!return!owner;
!!!!}
!!...
Optional...

Semicolons

public!class!Greeter!{
!!!!private!String!owner;
!!!!public!String!getOwner()!{
!!!!!!!!return!ow...
Optional...
public!class!Greeter!{
!!!!private!String!owner
!!!!public!String!getOwner()!{
!!!!!!!!return!owner
!!!!}
!!!!...
Optional...
public!class!Greeter!{
!!!!private!String!owner
!!!!public!String!getOwner()!{
!!!!!!!!return!owner
!!!!}
!!!!...
Optional...
public!class!Greeter!{
!!!!private!String!owner
!!!!public!String!getOwner()!{
!!!!!!!!return!owner
!!!!}
!!!!...
Optional...
public!class!Greeter!{
!!!!private!String!owner
!!!!public!String!getOwner()!{
!!!!!!!!return!owner
!!!!}
!!!!...
Optional...
public!class!Greeter!{
!!!!private!String!owner
!!!!public!String!getOwner()!{
!!!!!!!!return!owner
!!!!}
!!!!...
Optional...
public!class!Greeter!{
!!!!private!String!owner
!!!!public!String!getOwner()!{
!!!!!!!!******!owner
!!!!}
!!!!...
Optional...
public!class!Greeter!{
!!!!private!String!owner
!!!!public!String!getOwner()!{
!!!!!!!!******!owner
!!!!}

pub...
Optional...
******!class!Greeter!{
!!!!private!String!owner
!!!!******!String!getOwner()!{
!!!!!!!!******!owner
!!!!}
!!!!...
Optional...
******!class!Greeter!{
!!!!private!String!owner
!!!!******!String!getOwner()!{
!!!!!!!!******!owner
!!!!}
!!!!...
Optional...
******!class!Greeter!{
!!!!private!String!owner
!!!!******!String!getOwner()!{
!!!!!!!!******!owner
!!!!}
!!!!...
Optional...
******!class!Greeter!{
!!!!private!String!owner
!!!!******!String!getOwner()!{
!!!!!!!!******!owner
!!!!}
!!!!...
Optional...
******!class!Greeter!{
!!!!private!String!owner
!!!!******!String!getOwner()!{
!!!!!!!!******!owner
!!!!}
!!!!...
Optional...

verbose Java properties!

******!class!Greeter!{
!!!!private!String!owner
!!!!******!String!getOwner()!{
!!!!...
Optional...
******!class!Greeter!{
!!!!*******!String!owner

!!!!*******String!greet(String!name)!{
!!!!!!!!******!"Hello!...
Optional...
******!class!Greeter!{
!!!!*******!String!owner

!!!!*******String!greet(String!name)!{
!!!!!!!!******!"Hello!...
Optional...
******!class!Greeter!{
!!!!*******!String!owner

!!!!*******String!greet(String!name)!{
!!!!!!!!******!"Hello!...
Optional...
******!class!Greeter!{
!!!!*******!String!owner

Named argument
!!!!*******String!greet(String!name)!{
!!!!!!!...
Optional...
******!class!Greeter!{
!!!!*******!String!owner

!!!!*******String!greet(String!name)!{
!!!!!!!!******!"Hello!...
Optional...
******!class!Greeter!{
!!!!*******!String!owner

Interpolated strings!
(aka GStrings)
!!!!*******String!greet(...
Optional...
******!class!Greeter!{
!!!!*******!String!owner

!!!!*******String!greet(String!name)!{
!!!!!!!!******!"Hello!...
Optional...
******!class!Greeter!{
!!!!*******!String!owner

Let’s reformat that
mess of whitespace!

!!!!*******String!gr...
Optional...
class!Greeter!{
***!String!owner
****String!greet(String!name)!{
******!"Hello!${name},!I!am!${owner}"
!!!!}
}...
Optional...
public!class!Greeter!{
!!!!private!String!owner;
!!!!public!String!getOwner()!{
class!Greeter!{
!!!!!!!!return...
Optional...
class!Greeter!{
***!String!owner
****String!greet(String!name)!{
******!"Hello!${name},!I!am!${owner}"
!!!!}
}...
Native syntax constructs
• Closures
• Lists
• Maps
• Regular expressions
• Ranges
39
Closures
• Functions as first-class citizen of the language

40
Lists
def!list!=!['a',!'b',!'c']
list!<<!'d'
assert!list.contains('d')
assert!list.findAll!{!it.startsWith!'a'!}.size()!==...
Lists

List definition

def!list!=!['a',!'b',!'c']
list!<<!'d'
assert!list.contains('d')
assert!list.findAll!{!it.startsWi...
Lists

List definition

def!list!=!['a',!'b',!'c']

Append an element
(operator overloading)

list!<<!'d'
assert!list.cont...
Lists

List definition

def!list!=!['a',!'b',!'c']

Append an element
(operator overloading)

list!<<!'d'
assert!list.cont...
Maps
def!map!=![name:!'Guillaume',!age:!36]
map.daughters!=!['Marion',!'Erine']
assert!map['daughters'].contains('Marion')...
Maps

Map definition

def!map!=![name:!'Guillaume',!age:!36]
map.daughters!=!['Marion',!'Erine']
assert!map['daughters'].c...
Maps

Map definition

def!map!=![name:!'Guillaume',!age:!36]
map.daughters!=!['Marion',!'Erine']
assert!map['daughters'].c...
Maps

Map definition

def!map!=![name:!'Guillaume',!age:!36]
map.daughters!=!['Marion',!'Erine']
assert!map['daughters'].c...
Regular expressions
def!pattern!=!~/.*foo.*/
assert!"Alibaba"!==~!/.*(ba){2}/
def!matcher!=!"Superman"!=~!/([AcZ][acz]+)ma...
Regular expressions
Pattern
def!pattern!=!~/.*foo.*/
assert!"Alibaba"!==~!/.*(ba){2}/
def!matcher!=!"Superman"!=~!/([AcZ][...
Regular expressions
Pattern

Match

def!pattern!=!~/.*foo.*/
assert!"Alibaba"!==~!/.*(ba){2}/
def!matcher!=!"Superman"!=~!...
Regular expressions
Pattern

Match

def!pattern!=!~/.*foo.*/
assert!"Alibaba"!==~!/.*(ba){2}/

Find

def!matcher!=!"Superm...
Regular expressions
Pattern

Match

def!pattern!=!~/.*foo.*/
assert!"Alibaba"!==~!/.*(ba){2}/

Find

def!matcher!=!"Superm...
Ranges
def!range!=!'a'..'z'
assert!range.contains('m')
assert!range.contains('z')
def!exclusive!=!1..<10
assert!!exclusive...
Ranges

Range

def!range!=!'a'..'z'
assert!range.contains('m')
assert!range.contains('z')
def!exclusive!=!1..<10
assert!!e...
Ranges

Range

def!range!=!'a'..'z'
assert!range.contains('m')
assert!range.contains('z')
def!exclusive!=!1..<10

Excluded...
Ranges

Range

def!range!=!'a'..'z'
assert!range.contains('m')
assert!range.contains('z')
def!exclusive!=!1..<10

Excluded...
Ranges

Range

def!range!=!'a'..'z'
assert!range.contains('m')
assert!range.contains('z')
def!exclusive!=!1..<10

Excluded...
Strings, GStrings, multiline strings

def!name!=!'Groovy'
def!tmpl!=!"""
!!!!Dear!Mr!${name},
!!!!You're!the!winner!of!the...
Strings, GStrings, multiline strings
Plain java.lang.String
def!name!=!'Groovy'
def!tmpl!=!"""
!!!!Dear!Mr!${name},
!!!!Yo...
Strings, GStrings, multiline strings
Plain java.lang.String
def!name!=!'Groovy'
Multiline string with
def!tmpl!=!"""
expre...
Surprising numbers...

System.out.println(!2.0!c!1.1!);

46
Surprising numbers...

System.out.println(!2.0!c!1.1!);
0.8999999999999999

46
Surprising numbers...

System.out.println(!2.0!c!1.1!);
0.8999999999999999

46
Surprising numbers...

System.out.println(!3!/!2!);

47
Surprising numbers...

System.out.println(!3!/!2!);
1

47
Surprising numbers...

System.out.println(!3!/!2!);
1

47
BigDecimal by default!

assert!2.0!c!1.1!==!0.9
assert!3!/!2!==!1.5

48
BigDecimal by default!

assert!2.0!c!1.1!==!0.9
assert!3!/!2!==!1.5
One of the reasons why microbenchmarks sometimes showe...
BigDecimal by default!

assert!2.0!c!1.1!==!0.9
assert!3!/!2!==!1.5
One of the reasons why microbenchmarks sometimes showe...
Powerful
switch / case
on steroids
Powerful
switch / case
on steroids

switch(obj)!{
!!!!case!123:!!!!!!!!!!!!!!!!!!"number!123";!!!!!!!!break
!!!!case!"abc"...
Named arguments

move!obj,!x:!3,!y:!4

50
Named arguments

move!obj,!x:!3,!y:!4
Normal argument

50
Named arguments

move!obj,!x:!3,!y:!4
Normal argument

50

Named argument
Named arguments

move!obj,!x:!3,!y:!4
Normal argument
Calls:
move(Map m, Object)

50

Named argument
Command chains
• Ability to chain method calls
without parentheses and dots

51
Command chains
• Ability to chain method calls
without parentheses and dots

move!forward!at!3.km/h

51
Command chains
• Ability to chain method calls
without parentheses and dots

move!forward!at!3.km/h
Actually equivalent to...
Named arguments and command chains

check!that:!vodka!tastes!good

52
Named arguments and command chains

check!that:!vodka!tastes!good
Will call:
check(that: vodka).tastes(good)

52
Multiple assignment and destructuring
def!(a,!b)!=!['A',!'B']
(a,!b)!=![b,!a]
def!(int!i,!int!j)!=![1,!2]
def!geocode(Stri...
Multiple assignment and destructuring
def!(a,!b)!=!['A',!'B']
(a,!b)!=![b,!a]

Classic « swap »

def!(int!i,!int!j)!=![1,!...
Multiple assignment and destructuring
def!(a,!b)!=!['A',!'B']

With types

(a,!b)!=![b,!a]

Classic « swap »

def!(int!i,!...
Multiple assignment and destructuring
def!(a,!b)!=!['A',!'B']

With types

(a,!b)!=![b,!a]

Classic « swap »

def!(int!i,!...
Multiple assignment and destructuring
def!(a,!b)!=!['A',!'B']

With types

(a,!b)!=![b,!a]

Classic « swap »

def!(int!i,!...
Multiple assignment and destructuring
class!Point!{
!!!!double!x,!y
!!!!double!getAt(int!idx)!{
!!!!!!!!if!(idx!==!0)!x
!!...
Multiple assignment and destructuring
class!Point!{
!!!!double!x,!y

Method signature
convention: getAt(int)

!!!!double!g...
Multiple assignment and destructuring
class!Point!{
!!!!double!x,!y

Method signature
convention: getAt(int)

!!!!double!g...
Builders and GPath expressions
import!groovy.xml.*
new!MarkupBuilder().html!{
!!!!head!{
!!!!!!!!title!"The!Script!Bowl"
!...
Builders and GPath expressions
import!groovy.xml.*

Hierarchical data
representation

new!MarkupBuilder().html!{
!!!!head!...
Builders and GPath expressions
import!groovy.xml.*

Hierarchical data
representation

new!MarkupBuilder().html!{
!!!!head!...
Builders and GPath expressions
import!groovy.xml.*

Hierarchical data
representation

new!MarkupBuilder().html!{
!!!!head!...
Power asserts
def!(a,!b,!c)!=![20,!30,!40]
assert!a!*!(b!c!1)!/!10!==!3!*!c!/!2!+!1

56
Power asserts
def!(a,!b,!c)!=![20,!30,!40]
assert!a!*!(b!c!1)!/!10!==!3!*!c!/!2!+!1
Assertion!failed:!
assert!a!*!(b!c!1)!...
Null handling
• Groovy provides a more descriptive
NullPointerException than Java
• Safe navigation with ?.

57
The Truth,
the Groovy Truth!
The Truth,
the Groovy Truth!
And what if I could
customize the truth?
?:
?:
The Elvis
operator!
Towards Elvis...

60
Towards Elvis...
def!(x,!y)!=!['MacBook!Pro',!'unknown']

60
Towards Elvis...
def!(x,!y)!=!['MacBook!Pro',!'unknown']
if*(x!!=!null!&&!x.size()!>!0)!x!else!y

60
Towards Elvis...
def!(x,!y)!=!['MacBook!Pro',!'unknown']
if*(x!!=!null!&&!x.size()!>!0)!x!else!y
if*(x!&&!x.size())!x!else...
Towards Elvis...
def!(x,!y)!=!['MacBook!Pro',!'unknown']
if*(x!!=!null!&&!x.size()!>!0)!x!else!y
if*(x!&&!x.size())!x!else...
Towards Elvis...
def!(x,!y)!=!['MacBook!Pro',!'unknown']
if*(x!!=!null!&&!x.size()!>!0)!x!else!y
if*(x!&&!x.size())!x!else...
Towards Elvis...
def!(x,!y)!=!['MacBook!Pro',!'unknown']
if*(x!!=!null!&&!x.size()!>!0)!x!else!y
if*(x!&&!x.size())!x!else...
Towards Elvis...
def!(x,!y)!=!['MacBook!Pro',!'unknown']
if*(x!!=!null!&&!x.size()!>!0)!x!else!y
if*(x!&&!x.size())!x!else...
Towards Elvis...
def!(x,!y)!=!['MacBook!Pro',!'unknown']
if*(x!!=!null!&&!x.size()!>!0)!x!else!y
if*(x!&&!x.size())!x!else...
Towards Elvis...
def!(x,!y)!=!['MacBook!Pro',!'unknown']
if*(x!!=!null!&&!x.size()!>!0)!x!else!y
if*(x!&&!x.size())!x!else...
AST transformations
• Abstract Syntax Tree
– in memory representation of your program
before being compiled into bytecode
...
Lots of AST transformations...
• Code generation
– @ToString, @EqualsAndHashCode, @Canonical,
@TupleConstructor, @InheritC...
Lots of AST transformations...
• Safer scripting
– @ConditionalInterrupt, @ThreadInterrupt,
@TimedInterupt

• Compiler dir...
Lots of AST transformations...
• Dependencies handling
– @Grab, @GrabConfig, @GrabExclude,
@GrabResolver

• Test assistance...
Immutability
• Implement immutability
by the book
– final class
– tuple-style constructor
– private final backing fields
– de...
Immutability
• Implement immutability
by the book
– final class
Can be error-prone to
– tuple-style constructor
write immut...
Immutability
• A Person class with

public final class Person {
private final String name;
private final int age;
public P...
Immutability
• A Person class with

Damn
verbose
Java!

public final class Person {
private final String name;
private fin...
Immutability
• A Person class with

Damn
verbose
Java!

public final class Person {
private final String name;
private fin...
@Immutable

import!groovy.transform.*
@Immutable
class!Person!{
!!!!String!name
!!!!int!age
}
67
Memoization
• Cache the result of previous invocations
of closures or methods with the same set
of argument values
import!...
Memoization
• Cache the result of previous invocations
of closures or methods with the same set
of argument values
import!...
Groovy allows you
to be lazy
Groovy allows you
to be lazy

The compiler will do
the job for you
Groovy allows you
to be lazy

More concise, more
readable code

The compiler will do
the job for you
Groovy allows you
to be lazy

More concise, more
readable code

The compiler will do
the job for you

Less stuff to mainta...
@TypeChecked and @CompileStatic
• Static type checking with @TypeChecked,
throws compilation errors on...
– typos in metho...
@TypeChecked and @CompileStatic
• Static type checking with @TypeChecked,
throws compilation errors on...
– typos in metho...
@TypeChecked and @CompileStatic
• Static type checking with @TypeChecked,
throws compilation errors on...
– typos in metho...
@TypeChecked and @CompileStatic
• What is type checked can also be compiled
statically with @CompileStatic
– generate the ...
Pi (π)
Fibonacci
quadrature
Java

191 ms

97 ms

3.6 s

2.x

Static
compilation

197 ms

101 ms

4.3 s

1.8
72

Binary
tre...
3
t
r
a
P

Superb
community!
A blossoming
Ecosystem
M
V
G
GVM
GVM
GROOVY
ENVIRONMENT

MANAGER
GVM: Groovy enVironment Manager
• The new kid on the block
– http://gvmtool.net/ — @gvmtool

• Manage parallel versions
of...
I’m Spock...
I’m Spock...
...the Spock testing
framework
I’m Spock...
...the Spock testing
framework
Spock example
@Grab('org.spockframework:spockccore:0.7cgroovyc2.0')
import!spock.lang.*
class!MathSpec!extends!Specificati...
Spock example

@Grab a dependency

@Grab('org.spockframework:spockccore:0.7cgroovyc2.0')
import!spock.lang.*
class!MathSpe...
Spock example

@Grab a dependency

@Grab('org.spockframework:spockccore:0.7cgroovyc2.0')
import!spock.lang.*
class!MathSpe...
Spock example

@Grab a dependency

@Grab('org.spockframework:spockccore:0.7cgroovyc2.0')
import!spock.lang.*

Meaningful t...
Spock example

@Grab a dependency

@Grab('org.spockframework:spockccore:0.7cgroovyc2.0')
import!spock.lang.*

Meaningful t...
Spock example

@Grab a dependency

@Grab('org.spockframework:spockccore:0.7cgroovyc2.0')
import!spock.lang.*

Meaningful t...
@GrabResolver("https://oss.jfrog.org/artifactory/repo")
@Grab("org.ratpackcframework:ratpackcgroovy:0.9.0cSNAPSHOT")
impor...
@GrabResolver("https://oss.jfrog.org/artifactory/repo")
@Grab("org.ratpackcframework:ratpackcgroovy:0.9.0cSNAPSHOT")
impor...
Geb
• Browser automation solution
• WebDriver + jQuery selectors + Groovy
• Handy for
– scripting, scraping, automation......
Geb — Example
import!geb.Browser
Browser.drive!{
!!!!go!"http://myapp.com/login"
!!!!assert!$("h1").text()!==!"Please!Logi...
Geb — Example

Drive the browser
to this site

import!geb.Browser
Browser.drive!{
!!!!go!"http://myapp.com/login"
!!!!asse...
Geb — Example

Drive the browser
to this site

import!geb.Browser
Browser.drive!{
!!!!go!"http://myapp.com/login"

Check t...
Geb — Example

Drive the browser
to this site

import!geb.Browser
Browser.drive!{
!!!!go!"http://myapp.com/login"

Check t...
Geb — Example

Drive the browser
to this site

import!geb.Browser
Browser.drive!{
!!!!go!"http://myapp.com/login"

Check t...
Geb — Example

Drive the browser
to this site

import!geb.Browser
Browser.drive!{
!!!!go!"http://myapp.com/login"

Check t...
Geb — With page objects and Spock
import!geb.spock.GebSpec
class!GoogleWikipediaSpec!extends!GebSpec!{
!!!!def!"first!resu...
Geb — With page objects and Spock
import!geb.spock.GebSpec
class!GoogleWikipediaSpec!extends!GebSpec!{

With page objects
...
Geb — With page objects and Spock
import!geb.spock.GebSpec
class!GoogleWikipediaSpec!extends!GebSpec!{

With page objects
...
Geb — With page objects and Spock
import!geb.spock.GebSpec
class!GoogleWikipediaSpec!extends!GebSpec!{

With page objects
...
4
t
r
a
P

Summary
Java’s best friend
• Java derived syntax
– Flat learning curve
– Easy to learn

• But goes beyond Java
– Concise, expressi...
Groovy’s nature
• Object oriented dynamic language...
• But...
– as type safe as you want it — static type checking
– as f...
Groovy use cases
• Scripting tasks, build automation
• Extension points for customizing/configuring apps
• Business languag...
What makes groovy groovy   codeurs en seine - 2013 - light size
What makes groovy groovy   codeurs en seine - 2013 - light size
What makes groovy groovy   codeurs en seine - 2013 - light size
What makes groovy groovy   codeurs en seine - 2013 - light size
What makes groovy groovy   codeurs en seine - 2013 - light size
What makes groovy groovy   codeurs en seine - 2013 - light size
What makes groovy groovy   codeurs en seine - 2013 - light size
What makes groovy groovy   codeurs en seine - 2013 - light size
What makes groovy groovy   codeurs en seine - 2013 - light size
What makes groovy groovy   codeurs en seine - 2013 - light size
What makes groovy groovy   codeurs en seine - 2013 - light size
What makes groovy groovy   codeurs en seine - 2013 - light size
What makes groovy groovy   codeurs en seine - 2013 - light size
What makes groovy groovy   codeurs en seine - 2013 - light size
What makes groovy groovy   codeurs en seine - 2013 - light size
What makes groovy groovy   codeurs en seine - 2013 - light size
What makes groovy groovy   codeurs en seine - 2013 - light size
What makes groovy groovy   codeurs en seine - 2013 - light size
What makes groovy groovy   codeurs en seine - 2013 - light size
What makes groovy groovy   codeurs en seine - 2013 - light size
What makes groovy groovy   codeurs en seine - 2013 - light size
What makes groovy groovy   codeurs en seine - 2013 - light size
What makes groovy groovy   codeurs en seine - 2013 - light size
What makes groovy groovy   codeurs en seine - 2013 - light size
What makes groovy groovy   codeurs en seine - 2013 - light size
What makes groovy groovy   codeurs en seine - 2013 - light size
What makes groovy groovy   codeurs en seine - 2013 - light size
What makes groovy groovy   codeurs en seine - 2013 - light size
What makes groovy groovy   codeurs en seine - 2013 - light size
Upcoming SlideShare
Loading in...5
×

What makes groovy groovy codeurs en seine - 2013 - light size

357

Published on

Groovy n'est pas un nouveau venu dans l'arène des langages alternatifs pour la JVM, mais avec plus d'1.7 millions de téléchargements par an, c'est clairement le plus utilisé aujourd'hui !
Mais pourquoi choisir Groovy pour vos projets ? Que peut-il vous apporter ?
- une courbe d'apprentissage minime
- son intégration transparente avec Java pour mixer Groovy et Java ensemble
- une syntax malléable, concise et lisible adaptée aux Domain-Specific Languages
- une approche pragmatique sur le typage
- un riche écosystème de projets, comme Grails, Gradle, GPars, Spock, Geb, etc...
Dans cette session, vous découvrirez comment tout s'articule dans l'univers Groovy, ainsi que où, quand, comment vous pourrez tirer avantage de Groovy pour améliorer votre productivité.

0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
357
On Slideshare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
15
Comments
0
Likes
1
Embeds 0
No embeds

No notes for slide

Transcript of "What makes groovy groovy codeurs en seine - 2013 - light size"

  1. 1. What makes Groovy groovy? Guillaume Laforge @glaforge © 2013 Guillaume Laforge. All rights reserved. Do not distribute without permission.
  2. 2. Guillaume Laforge Groovy project lead at . @glaforge http://glaforge.appspot.com
  3. 3. Les Cast Codeurs
  4. 4. 1 t r a P The Groovy vision
  5. 5. Simplify the life of (Java) developers
  6. 6. Great for scripting
  7. 7. Great for scripting Fit for DomainSpecific Languages
  8. 8. Great for scripting Fit for DomainSpecific Languages Most seamless integration & interoperability wih java!
  9. 9. It’s so easy to learn!
  10. 10. Groovy as a Java superset It’s so easy to learn!
  11. 11. class!MathSpec!extends!Specification!{ !!!!def!"maximum!of!two!numbers"()!{ ******expect: !!!!!!!!Math.max(a,!b)!==!c ******where: !!!!!!!!a!|!b!||!c !!!!!!!!1!|!3!||!3 !!!!!!!!7!|!4!||!4 !!!!!!!!0!|!0!||!0 !!!!} }
  12. 12. As safe and fast as Java with static type checking & compilation
  13. 13. As safe and fast as Java with static type checking & compilation
  14. 14. new!MarkupBuilder().html!{ !!!!head!{ !!!!!!!!title!"The!Script!Bowl" !!!!} !!!!body!{ !!!!!!!!div(class:!"banner")!{ !!!!!!!!!!!!p!"Groovy!rocks!" !!!!!!!!} !!!!} }
  15. 15. move!forward!at!3.km/h Expressive, Concise, Readable
  16. 16. Speaking of conciseness... A full Spring app in the span of a tweet! @RestController class!App!{ !!!!@RequestMapping("/") !!!!String!home()!{!"Hello!World!"!} }
  17. 17. • SmartThings • Carriots Services • European Patent Office • Amadeus IoT Financial • Crédit Suisse • JPMorgan • Fanny Mae • Mutual of Omaha • Hypoport 20 • Energy Transfer • National Cancer Inst. • IRSN Internet • Netflix • LinkedIn • Google Technology Who’s using Groovy?
  18. 18. 2 t r a P Cool Groovy gems
  19. 19. Most Java code is also valid Groovy code!
  20. 20. Most Java code is also valid Groovy code! Any Java developer is a Groovy developer!
  21. 21. Flat learning curve
  22. 22. Flat learning curve Easy to learn
  23. 23. Scripts versus Classes public!class!Main!{ !!!!public!static!void!main(String[]!args)!{ !!!!!!!!System.out.println("Hello"); !!!!} } vs 24
  24. 24. Scripts versus Classes public!class!Main!{ !!!!public!static!void!main(String[]!args)!{ !!!!!!!!System.out.println("Hello"); !!!!} } vs println!"Hello" 24
  25. 25. Optional
  26. 26. Semicolons Optional
  27. 27. Semicolons Parentheses Optional
  28. 28. Semicolons Parentheses Optional return keyword
  29. 29. Semicolons Parentheses Optional return keyword public keyword
  30. 30. Semicolons Parentheses Optional Typing! return keyword public keyword
  31. 31. Optional... public!class!Greeter!{ !!!!private!String!owner; !!!!public!String!getOwner()!{ !!!!!!!!return!owner; !!!!} !!!!public!void!setOwner(String!owner)!{ !!!!!!!!this.owner!=!owner; !!!!} !!!!public*String!greet(String!name)!{ !!!!!!!!return!"Hello!"!+!name!+!",!I!am!"!+!owner; !!!!} } Greeter!greeter!=!new!Greeter(); greeter.setOwner("Guillaume"); System.out.println(greeter.greet("Marion")); 26
  32. 32. Optional... Semicolons public!class!Greeter!{ !!!!private!String!owner; !!!!public!String!getOwner()!{ !!!!!!!!return!owner; !!!!} !!!!public!void!setOwner(String!owner)!{ !!!!!!!!this.owner!=!owner; !!!!} !!!!public*String!greet(String!name)!{ !!!!!!!!return!"Hello!"!+!name!+!",!I!am!"!+!owner; !!!!} } Greeter!greeter!=!new!Greeter(); greeter.setOwner("Guillaume"); System.out.println(greeter.greet("Marion")); 26
  33. 33. Optional... public!class!Greeter!{ !!!!private!String!owner !!!!public!String!getOwner()!{ !!!!!!!!return!owner !!!!} !!!!public!void!setOwner(String!owner)!{ !!!!!!!!this.owner!=!owner !!!!} !!!!public*String!greet(String!name)!{ !!!!!!!!return!"Hello!"!+!name!+!",!I!am!"!+!owner !!!!} } Greeter!greeter!=!new!Greeter() greeter.setOwner("Guillaume") System.out.println(greeter.greet("Marion")) 27
  34. 34. Optional... public!class!Greeter!{ !!!!private!String!owner !!!!public!String!getOwner()!{ !!!!!!!!return!owner !!!!} !!!!public!void!setOwner(String!owner)!{ !!!!!!!!this.owner!=!owner !!!!} !!!!public*String!greet(String!name)!{ !!!!!!!!return!"Hello!"!+!name!+!",!I!am!"!+!owner !!!!} } Greeter!greeter!=!new!Greeter() greeter.setOwner("Guillaume") System.out.println(greeter.greet("Marion")) 28
  35. 35. Optional... public!class!Greeter!{ !!!!private!String!owner !!!!public!String!getOwner()!{ !!!!!!!!return!owner !!!!} !!!!public!void!setOwner(String!owner)!{ !!!!!!!!this.owner!=!owner !!!!} !!!!public*String!greet(String!name)!{ !!!!!!!!return!"Hello!"!+!name!+!",!I!am!"!+!owner !!!!} } Parentheses Greeter!greeter!=!new!Greeter() greeter.setOwner("Guillaume") System.out.println(greeter.greet("Marion")) 28
  36. 36. Optional... public!class!Greeter!{ !!!!private!String!owner !!!!public!String!getOwner()!{ !!!!!!!!return!owner !!!!} !!!!public!void!setOwner(String!owner)!{ !!!!!!!!this.owner!=!owner !!!!} !!!!public*String!greet(String!name)!{ !!!!!!!!return!"Hello!"!+!name!+!",!I!am!"!+!owner !!!!} } Greeter!greeter!=!new!Greeter() greeter.setOwner!"Guillaume" System.out.println!greeter.greet("Marion") 29
  37. 37. Optional... public!class!Greeter!{ !!!!private!String!owner !!!!public!String!getOwner()!{ !!!!!!!!return!owner !!!!} !!!!public!void!setOwner(String!owner)!{ !!!!!!!!this.owner!=!owner !!!!} return keyword !!!!public*String!greet(String!name)!{ !!!!!!!!return!"Hello!"!+!name!+!",!I!am!"!+!owner !!!!} } Greeter!greeter!=!new!Greeter() greeter.setOwner!"Guillaume" System.out.println!greeter.greet("Marion") 29
  38. 38. Optional... public!class!Greeter!{ !!!!private!String!owner !!!!public!String!getOwner()!{ !!!!!!!!******!owner !!!!} !!!!public!void!setOwner(String!owner)!{ !!!!!!!!this.owner!=!owner !!!!} !!!!public*String!greet(String!name)!{ !!!!!!!!******!"Hello!"!+!name!+!",!I!am!"!+!owner !!!!} } Greeter!greeter!=!new!Greeter() greeter.setOwner!"Guillaume" System.out.println!greeter.greet("Marion") 30
  39. 39. Optional... public!class!Greeter!{ !!!!private!String!owner !!!!public!String!getOwner()!{ !!!!!!!!******!owner !!!!} public keyword !!!!public!void!setOwner(String!owner)!{ !!!!!!!!this.owner!=!owner !!!!} !!!!public*String!greet(String!name)!{ !!!!!!!!******!"Hello!"!+!name!+!",!I!am!"!+!owner !!!!} } Greeter!greeter!=!new!Greeter() greeter.setOwner!"Guillaume" System.out.println!greeter.greet("Marion") 30
  40. 40. Optional... ******!class!Greeter!{ !!!!private!String!owner !!!!******!String!getOwner()!{ !!!!!!!!******!owner !!!!} !!!!******!void!setOwner(String!owner)!{ !!!!!!!!this.owner!=!owner !!!!} !!!!*******String!greet(String!name)!{ !!!!!!!!******!"Hello!"!+!name!+!",!I!am!"!+!owner !!!!} } Greeter!greeter!=!new!Greeter() greeter.setOwner!"Guillaume" System.out.println!greeter.greet("Marion") 31
  41. 41. Optional... ******!class!Greeter!{ !!!!private!String!owner !!!!******!String!getOwner()!{ !!!!!!!!******!owner !!!!} !!!!******!void!setOwner(String!owner)!{ !!!!!!!!this.owner!=!owner !!!!} !!!!*******String!greet(String!name)!{ !!!!!!!!******!"Hello!"!+!name!+!",!I!am!"!+!owner !!!!} } optional typing Greeter!greeter!=!new!Greeter() greeter.setOwner!"Guillaume" System.out.println!greeter.greet("Marion") 31
  42. 42. Optional... ******!class!Greeter!{ !!!!private!String!owner !!!!******!String!getOwner()!{ !!!!!!!!******!owner !!!!} !!!!******!void!setOwner(String!owner)!{ !!!!!!!!this.owner!=!owner !!!!} !!!!*******String!greet(String!name)!{ !!!!!!!!******!"Hello!"!+!name!+!",!I!am!"!+!owner !!!!} } def!!!!!greeter!=!new!Greeter() greeter.setOwner!"Guillaume" System.out.println!greeter.greet("Marion") 32
  43. 43. Optional... ******!class!Greeter!{ !!!!private!String!owner !!!!******!String!getOwner()!{ !!!!!!!!******!owner !!!!} !!!!******!void!setOwner(String!owner)!{ !!!!!!!!this.owner!=!owner !!!!} !!!!*******String!greet(String!name)!{ !!!!!!!!******!"Hello!"!+!name!+!",!I!am!"!+!owner !!!!} } handy println shortcut def!!!!!greeter!=!new!Greeter() greeter.setOwner!"Guillaume" System.out.println!greeter.greet("Marion") 32
  44. 44. Optional... ******!class!Greeter!{ !!!!private!String!owner !!!!******!String!getOwner()!{ !!!!!!!!******!owner !!!!} !!!!******!void!setOwner(String!owner)!{ !!!!!!!!this.owner!=!owner !!!!} !!!!*******String!greet(String!name)!{ !!!!!!!!******!"Hello!"!+!name!+!",!I!am!"!+!owner !!!!} } def!!!!!greeter!=!new!Greeter() greeter.setOwner!"Guillaume" !!!!!!!!!!!println!greeter.greet("Marion") 33
  45. 45. Optional... verbose Java properties! ******!class!Greeter!{ !!!!private!String!owner !!!!******!String!getOwner()!{ !!!!!!!!******!owner !!!!} !!!!******!void!setOwner(String!owner)!{ !!!!!!!!this.owner!=!owner !!!!} !!!!*******String!greet(String!name)!{ !!!!!!!!******!"Hello!"!+!name!+!",!I!am!"!+!owner !!!!} } def!!!!!greeter!=!new!Greeter() greeter.setOwner!"Guillaume" !!!!!!!!!!!println!greeter.greet("Marion") 33
  46. 46. Optional... ******!class!Greeter!{ !!!!*******!String!owner !!!!*******String!greet(String!name)!{ !!!!!!!!******!"Hello!"!+!name!+!",!I!am!"!+!owner !!!!} } def!!!!!greeter!=!new!Greeter() greeter.setOwner!"Guillaume" !!!!!!!!!!!println!greeter.greet("Marion") 34
  47. 47. Optional... ******!class!Greeter!{ !!!!*******!String!owner !!!!*******String!greet(String!name)!{ !!!!!!!!******!"Hello!"!+!name!+!",!I!am!"!+!owner !!!!} } Property notation def!!!!!greeter!=!new!Greeter() greeter.setOwner!"Guillaume" !!!!!!!!!!!println!greeter.greet("Marion") 34
  48. 48. Optional... ******!class!Greeter!{ !!!!*******!String!owner !!!!*******String!greet(String!name)!{ !!!!!!!!******!"Hello!"!+!name!+!",!I!am!"!+!owner !!!!} } def!!!!!greeter!=!new!Greeter() greeter.owner!!!!"Guillaume" !!!!!!!!!!!println!greeter.greet("Marion") 35
  49. 49. Optional... ******!class!Greeter!{ !!!!*******!String!owner Named argument !!!!*******String!greet(String!name)!{ !!!!!!!!******!"Hello!"!+!name!+!",!I!am!"!+!owner constructor !!!!} } def!!!!!greeter!=!new!Greeter() greeter.owner!!!!"Guillaume" !!!!!!!!!!!println!greeter.greet("Marion") 35
  50. 50. Optional... ******!class!Greeter!{ !!!!*******!String!owner !!!!*******String!greet(String!name)!{ !!!!!!!!******!"Hello!"!+!name!+!",!I!am!"!+!owner !!!!} } def!!!!!greeter!=!new!Greeter(owner:!"Guillaume") !!!!!!!!!!!println!greeter.greet("Marion") 36
  51. 51. Optional... ******!class!Greeter!{ !!!!*******!String!owner Interpolated strings! (aka GStrings) !!!!*******String!greet(String!name)!{ !!!!!!!!******!"Hello!"!+!name!+!",!I!am!"!+!owner !!!!} } def!!!!!greeter!=!new!Greeter(owner:!"Guillaume") !!!!!!!!!!!println!greeter.greet("Marion") 36
  52. 52. Optional... ******!class!Greeter!{ !!!!*******!String!owner !!!!*******String!greet(String!name)!{ !!!!!!!!******!"Hello!${name},!I!am!${owner}" !!!!} } def!!!!!greeter!=!new!Greeter(owner:!"Guillaume") !!!!!!!!!!!println!greeter.greet("Marion") 37
  53. 53. Optional... ******!class!Greeter!{ !!!!*******!String!owner Let’s reformat that mess of whitespace! !!!!*******String!greet(String!name)!{ !!!!!!!!******!"Hello!${name},!I!am!${owner}" !!!!} } def!!!!!greeter!=!new!Greeter(owner:!"Guillaume") !!!!!!!!!!!println!greeter.greet("Marion") 37
  54. 54. Optional... class!Greeter!{ ***!String!owner ****String!greet(String!name)!{ ******!"Hello!${name},!I!am!${owner}" !!!!} } def!greeter!=!new!Greeter(owner:!"Guillaume") println!greeter.greet("Marion") 38
  55. 55. Optional... public!class!Greeter!{ !!!!private!String!owner; !!!!public!String!getOwner()!{ class!Greeter!{ !!!!!!!!return!owner; ***!String!owner !!!!} ****String!greet(String!name)!{ !!!!public!void!setOwner(String!owner)!{ ******!"Hello!${name},!I!am!${owner}" !!!!!!!!this.owner!=!owner; !!!!} !!!!}} !!!!public*String!greet(String!name)!{ def!greeter!=!new!Greeter(owner:!"Guillaume") !!!!!!!!return!"Hello!"!+!name!+!",!I!am!"!+!owner; !!!!}println!greeter.greet("Marion") } Greeter!greeter!=!new!Greeter(); greeter.setOwner("Guillaume"); System.out.println(greeter.greet("Marion")); 38
  56. 56. Optional... class!Greeter!{ ***!String!owner ****String!greet(String!name)!{ ******!"Hello!${name},!I!am!${owner}" !!!!} } def!greeter!=!new!Greeter(owner:!"Guillaume") println!greeter.greet("Marion") 38
  57. 57. Native syntax constructs • Closures • Lists • Maps • Regular expressions • Ranges 39
  58. 58. Closures • Functions as first-class citizen of the language 40
  59. 59. Lists def!list!=!['a',!'b',!'c'] list!<<!'d' assert!list.contains('d') assert!list.findAll!{!it.startsWith!'a'!}.size()!==!1 assert!list.collect!{!it.toUpperCase()!}!==!['A',!'B',!'C',!'D'] assert!list.inject('')!{!a,!b!c>!a!+!b!}!==!'abcd' 41
  60. 60. Lists List definition def!list!=!['a',!'b',!'c'] list!<<!'d' assert!list.contains('d') assert!list.findAll!{!it.startsWith!'a'!}.size()!==!1 assert!list.collect!{!it.toUpperCase()!}!==!['A',!'B',!'C',!'D'] assert!list.inject('')!{!a,!b!c>!a!+!b!}!==!'abcd' 41
  61. 61. Lists List definition def!list!=!['a',!'b',!'c'] Append an element (operator overloading) list!<<!'d' assert!list.contains('d') assert!list.findAll!{!it.startsWith!'a'!}.size()!==!1 assert!list.collect!{!it.toUpperCase()!}!==!['A',!'B',!'C',!'D'] assert!list.inject('')!{!a,!b!c>!a!+!b!}!==!'abcd' 41
  62. 62. Lists List definition def!list!=!['a',!'b',!'c'] Append an element (operator overloading) list!<<!'d' assert!list.contains('d') assert!list.findAll!{!it.startsWith!'a'!}.size()!==!1 assert!list.collect!{!it.toUpperCase()!}!==!['A',!'B',!'C',!'D'] assert!list.inject('')!{!a,!b!c>!a!+!b!}!==!'abcd' Functional-style map / filter / reduce with closures 41
  63. 63. Maps def!map!=![name:!'Guillaume',!age:!36] map.daughters!=!['Marion',!'Erine'] assert!map['daughters'].contains('Marion') 42
  64. 64. Maps Map definition def!map!=![name:!'Guillaume',!age:!36] map.daughters!=!['Marion',!'Erine'] assert!map['daughters'].contains('Marion') 42
  65. 65. Maps Map definition def!map!=![name:!'Guillaume',!age:!36] map.daughters!=!['Marion',!'Erine'] assert!map['daughters'].contains('Marion') Indexed access 42
  66. 66. Maps Map definition def!map!=![name:!'Guillaume',!age:!36] map.daughters!=!['Marion',!'Erine'] assert!map['daughters'].contains('Marion') Property notation access 42 Indexed access
  67. 67. Regular expressions def!pattern!=!~/.*foo.*/ assert!"Alibaba"!==~!/.*(ba){2}/ def!matcher!=!"Superman"!=~!/([AcZ][acz]+)man/ assert!matcher[0][0]!==!'Superman' assert!matcher[0][1]!==!'Super' '75001!Paris'.find(/(d{5})s(w)+/)!{!match,!cp,!town!c> !!!!println!"The!Zip!code!of!${town}!is!${cp}" } 43
  68. 68. Regular expressions Pattern def!pattern!=!~/.*foo.*/ assert!"Alibaba"!==~!/.*(ba){2}/ def!matcher!=!"Superman"!=~!/([AcZ][acz]+)man/ assert!matcher[0][0]!==!'Superman' assert!matcher[0][1]!==!'Super' '75001!Paris'.find(/(d{5})s(w)+/)!{!match,!cp,!town!c> !!!!println!"The!Zip!code!of!${town}!is!${cp}" } 43
  69. 69. Regular expressions Pattern Match def!pattern!=!~/.*foo.*/ assert!"Alibaba"!==~!/.*(ba){2}/ def!matcher!=!"Superman"!=~!/([AcZ][acz]+)man/ assert!matcher[0][0]!==!'Superman' assert!matcher[0][1]!==!'Super' '75001!Paris'.find(/(d{5})s(w)+/)!{!match,!cp,!town!c> !!!!println!"The!Zip!code!of!${town}!is!${cp}" } 43
  70. 70. Regular expressions Pattern Match def!pattern!=!~/.*foo.*/ assert!"Alibaba"!==~!/.*(ba){2}/ Find def!matcher!=!"Superman"!=~!/([AcZ][acz]+)man/ assert!matcher[0][0]!==!'Superman' assert!matcher[0][1]!==!'Super' '75001!Paris'.find(/(d{5})s(w)+/)!{!match,!cp,!town!c> !!!!println!"The!Zip!code!of!${town}!is!${cp}" } 43
  71. 71. Regular expressions Pattern Match def!pattern!=!~/.*foo.*/ assert!"Alibaba"!==~!/.*(ba){2}/ Find def!matcher!=!"Superman"!=~!/([AcZ][acz]+)man/ assert!matcher[0][0]!==!'Superman' assert!matcher[0][1]!==!'Super' '75001!Paris'.find(/(d{5})s(w)+/)!{!match,!cp,!town!c> !!!!println!"The!Zip!code!of!${town}!is!${cp}" } Nice way to decompose the matched regions 43
  72. 72. Ranges def!range!=!'a'..'z' assert!range.contains('m') assert!range.contains('z') def!exclusive!=!1..<10 assert!!exclusive.contains(10) def!reverse!=!10..0 assert!reverse[0]!==!10 assert!reverse[c1]!==!0 44
  73. 73. Ranges Range def!range!=!'a'..'z' assert!range.contains('m') assert!range.contains('z') def!exclusive!=!1..<10 assert!!exclusive.contains(10) def!reverse!=!10..0 assert!reverse[0]!==!10 assert!reverse[c1]!==!0 44
  74. 74. Ranges Range def!range!=!'a'..'z' assert!range.contains('m') assert!range.contains('z') def!exclusive!=!1..<10 Excluded upper bound assert!!exclusive.contains(10) def!reverse!=!10..0 assert!reverse[0]!==!10 assert!reverse[c1]!==!0 44
  75. 75. Ranges Range def!range!=!'a'..'z' assert!range.contains('m') assert!range.contains('z') def!exclusive!=!1..<10 Excluded upper bound assert!!exclusive.contains(10) def!reverse!=!10..0 assert!reverse[0]!==!10 assert!reverse[c1]!==!0 44 Reverse range
  76. 76. Ranges Range def!range!=!'a'..'z' assert!range.contains('m') assert!range.contains('z') def!exclusive!=!1..<10 Excluded upper bound assert!!exclusive.contains(10) def!reverse!=!10..0 assert!reverse[0]!==!10 assert!reverse[c1]!==!0 44 Reverse range Negative index count from the end
  77. 77. Strings, GStrings, multiline strings def!name!=!'Groovy' def!tmpl!=!""" !!!!Dear!Mr!${name}, !!!!You're!the!winner!of!the!lottery! !!!!Yours!sincerly, !!!!Dave """ assert!tmpl.toString().contains('Groovy') 45
  78. 78. Strings, GStrings, multiline strings Plain java.lang.String def!name!=!'Groovy' def!tmpl!=!""" !!!!Dear!Mr!${name}, !!!!You're!the!winner!of!the!lottery! !!!!Yours!sincerly, !!!!Dave """ assert!tmpl.toString().contains('Groovy') 45
  79. 79. Strings, GStrings, multiline strings Plain java.lang.String def!name!=!'Groovy' Multiline string with def!tmpl!=!""" expression interpolation !!!!Dear!Mr!${name}, !!!!You're!the!winner!of!the!lottery! !!!!Yours!sincerly, !!!!Dave """ assert!tmpl.toString().contains('Groovy') 45
  80. 80. Surprising numbers... System.out.println(!2.0!c!1.1!); 46
  81. 81. Surprising numbers... System.out.println(!2.0!c!1.1!); 0.8999999999999999 46
  82. 82. Surprising numbers... System.out.println(!2.0!c!1.1!); 0.8999999999999999 46
  83. 83. Surprising numbers... System.out.println(!3!/!2!); 47
  84. 84. Surprising numbers... System.out.println(!3!/!2!); 1 47
  85. 85. Surprising numbers... System.out.println(!3!/!2!); 1 47
  86. 86. BigDecimal by default! assert!2.0!c!1.1!==!0.9 assert!3!/!2!==!1.5 48
  87. 87. BigDecimal by default! assert!2.0!c!1.1!==!0.9 assert!3!/!2!==!1.5 One of the reasons why microbenchmarks sometimes showed Groovy to be slow... 48
  88. 88. BigDecimal by default! assert!2.0!c!1.1!==!0.9 assert!3!/!2!==!1.5 One of the reasons why microbenchmarks sometimes showed Groovy to be slow... 48 But you can use doubles & floats for performance, with ‘d’ or ‘f ’ suffixes or with explicit type
  89. 89. Powerful switch / case on steroids
  90. 90. Powerful switch / case on steroids switch(obj)!{ !!!!case!123:!!!!!!!!!!!!!!!!!!"number!123";!!!!!!!!break !!!!case!"abc":!!!!!!!!!!!!!!!!"string!abc";!!!!!!!!break !!!!case!String:!!!!!!!!!!!!!!!"is!a!string";!!!!!!!break !!!!case![1,!2,!3]:!!!!!!!!!!!!"contained!in!list";!break !!!!case!~/.*o+.*/:!!!!!!!!!!!!"match!the!regex";!!!break !!!!case!{!it.isUpperCase()!}:!"closure!criteria";!!break ****default:!"unknown" }
  91. 91. Named arguments move!obj,!x:!3,!y:!4 50
  92. 92. Named arguments move!obj,!x:!3,!y:!4 Normal argument 50
  93. 93. Named arguments move!obj,!x:!3,!y:!4 Normal argument 50 Named argument
  94. 94. Named arguments move!obj,!x:!3,!y:!4 Normal argument Calls: move(Map m, Object) 50 Named argument
  95. 95. Command chains • Ability to chain method calls without parentheses and dots 51
  96. 96. Command chains • Ability to chain method calls without parentheses and dots move!forward!at!3.km/h 51
  97. 97. Command chains • Ability to chain method calls without parentheses and dots move!forward!at!3.km/h Actually equivalent to: move(forward).at(3.getKm().div(h)) 51
  98. 98. Named arguments and command chains check!that:!vodka!tastes!good 52
  99. 99. Named arguments and command chains check!that:!vodka!tastes!good Will call: check(that: vodka).tastes(good) 52
  100. 100. Multiple assignment and destructuring def!(a,!b)!=!['A',!'B'] (a,!b)!=![b,!a] def!(int!i,!int!j)!=![1,!2] def!geocode(String!place)!{ !!!!return![45.4,!2.3] } def!(la,!lo)!=!geocode("Paris") assert!la!==!45.4!&&!lo!==!2.3 53
  101. 101. Multiple assignment and destructuring def!(a,!b)!=!['A',!'B'] (a,!b)!=![b,!a] Classic « swap » def!(int!i,!int!j)!=![1,!2] def!geocode(String!place)!{ !!!!return![45.4,!2.3] } def!(la,!lo)!=!geocode("Paris") assert!la!==!45.4!&&!lo!==!2.3 53
  102. 102. Multiple assignment and destructuring def!(a,!b)!=!['A',!'B'] With types (a,!b)!=![b,!a] Classic « swap » def!(int!i,!int!j)!=![1,!2] def!geocode(String!place)!{ !!!!return![45.4,!2.3] } def!(la,!lo)!=!geocode("Paris") assert!la!==!45.4!&&!lo!==!2.3 53
  103. 103. Multiple assignment and destructuring def!(a,!b)!=!['A',!'B'] With types (a,!b)!=![b,!a] Classic « swap » def!(int!i,!int!j)!=![1,!2] Method returning a list def!geocode(String!place)!{ !!!!return![45.4,!2.3] } def!(la,!lo)!=!geocode("Paris") assert!la!==!45.4!&&!lo!==!2.3 53
  104. 104. Multiple assignment and destructuring def!(a,!b)!=!['A',!'B'] With types (a,!b)!=![b,!a] Classic « swap » def!(int!i,!int!j)!=![1,!2] Method returning a list def!geocode(String!place)!{ !!!!return![45.4,!2.3] } Destructuring def!(la,!lo)!=!geocode("Paris") assert!la!==!45.4!&&!lo!==!2.3 53
  105. 105. Multiple assignment and destructuring class!Point!{ !!!!double!x,!y !!!!double!getAt(int!idx)!{ !!!!!!!!if!(idx!==!0)!x !!!!!!!!else!if!(idx!==!1)!y !!!!!!!!else!throw!new!Exception("Wrong!index") !!!!} } def!(x,!y)!=!new!Point(x:!48.3,!y:!3.5) assert!x!==!48.3!&&!y!==!3.5 54
  106. 106. Multiple assignment and destructuring class!Point!{ !!!!double!x,!y Method signature convention: getAt(int) !!!!double!getAt(int!idx)!{ !!!!!!!!if!(idx!==!0)!x !!!!!!!!else!if!(idx!==!1)!y !!!!!!!!else!throw!new!Exception("Wrong!index") !!!!} } def!(x,!y)!=!new!Point(x:!48.3,!y:!3.5) assert!x!==!48.3!&&!y!==!3.5 54
  107. 107. Multiple assignment and destructuring class!Point!{ !!!!double!x,!y Method signature convention: getAt(int) !!!!double!getAt(int!idx)!{ !!!!!!!!if!(idx!==!0)!x !!!!!!!!else!if!(idx!==!1)!y !!!!!!!!else!throw!new!Exception("Wrong!index") !!!!} } Transparent destructuring def!(x,!y)!=!new!Point(x:!48.3,!y:!3.5) assert!x!==!48.3!&&!y!==!3.5 54
  108. 108. Builders and GPath expressions import!groovy.xml.* new!MarkupBuilder().html!{ !!!!head!{ !!!!!!!!title!"The!Script!Bowl" !!!!} !!!!body!{ !!!!!!!!div(class:!"banner")!{ !!!!!!!!!!!!p!"Groovy!won!" !!!!!!!!} !!!!} } 55
  109. 109. Builders and GPath expressions import!groovy.xml.* Hierarchical data representation new!MarkupBuilder().html!{ !!!!head!{ !!!!!!!!title!"The!Script!Bowl" !!!!} !!!!body!{ !!!!!!!!div(class:!"banner")!{ !!!!!!!!!!!!p!"Groovy!won!" !!!!!!!!} !!!!} } 55
  110. 110. Builders and GPath expressions import!groovy.xml.* Hierarchical data representation new!MarkupBuilder().html!{ !!!!head!{ !!!!!!!!title!"The!Script!Bowl" !!!!} !!!!body!{ !!!!!!!!div(class:!"banner")!{ !!!!!!!!!!!!p!"Groovy!won!" !!!!!!!!} Closure blocks delimiting !!!!} the structure } 55
  111. 111. Builders and GPath expressions import!groovy.xml.* Hierarchical data representation new!MarkupBuilder().html!{ !!!!head!{ !!!!!!!!title!"The!Script!Bowl" !!!!} Attributes !!!!body!{ !!!!!!!!div(class:!"banner")!{ !!!!!!!!!!!!p!"Groovy!won!" !!!!!!!!} Closure blocks delimiting !!!!} the structure } 55
  112. 112. Power asserts def!(a,!b,!c)!=![20,!30,!40] assert!a!*!(b!c!1)!/!10!==!3!*!c!/!2!+!1 56
  113. 113. Power asserts def!(a,!b,!c)!=![20,!30,!40] assert!a!*!(b!c!1)!/!10!==!3!*!c!/!2!+!1 Assertion!failed:! assert!a!*!(b!c!1)!/!10!==!3!*!c!/!2!+!1 !!!!!!!|!|!!|!|!!!!|!!!!|!!!!|!|!|!!!| !!!!!!!|!580|!29!!!58!!!false|!|!60!!61 !!!!!!!20!!!30!!!!!!!!!!!!!!!|!40 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!120 ! at!script1.run(script1t.groovy:4) 56
  114. 114. Null handling • Groovy provides a more descriptive NullPointerException than Java • Safe navigation with ?. 57
  115. 115. The Truth, the Groovy Truth!
  116. 116. The Truth, the Groovy Truth! And what if I could customize the truth?
  117. 117. ?:
  118. 118. ?: The Elvis operator!
  119. 119. Towards Elvis... 60
  120. 120. Towards Elvis... def!(x,!y)!=!['MacBook!Pro',!'unknown'] 60
  121. 121. Towards Elvis... def!(x,!y)!=!['MacBook!Pro',!'unknown'] if*(x!!=!null!&&!x.size()!>!0)!x!else!y 60
  122. 122. Towards Elvis... def!(x,!y)!=!['MacBook!Pro',!'unknown'] if*(x!!=!null!&&!x.size()!>!0)!x!else!y if*(x!&&!x.size())!x!else!y 60
  123. 123. Towards Elvis... def!(x,!y)!=!['MacBook!Pro',!'unknown'] if*(x!!=!null!&&!x.size()!>!0)!x!else!y if*(x!&&!x.size())!x!else!y if*(x)!x!else!y 60
  124. 124. Towards Elvis... def!(x,!y)!=!['MacBook!Pro',!'unknown'] if*(x!!=!null!&&!x.size()!>!0)!x!else!y if*(x!&&!x.size())!x!else!y if*(x)!x!else!y x!?!x!:!y 60
  125. 125. Towards Elvis... def!(x,!y)!=!['MacBook!Pro',!'unknown'] if*(x!!=!null!&&!x.size()!>!0)!x!else!y if*(x!&&!x.size())!x!else!y if*(x)!x!else!y x!?!x!:!y x!?:!y 60
  126. 126. Towards Elvis... def!(x,!y)!=!['MacBook!Pro',!'unknown'] if*(x!!=!null!&&!x.size()!>!0)!x!else!y if*(x!&&!x.size())!x!else!y if*(x)!x!else!y x!?!x!:!y x!?:!y Null, empty, zerosized... false, otherwise true! 60
  127. 127. Towards Elvis... def!(x,!y)!=!['MacBook!Pro',!'unknown'] if*(x!!=!null!&&!x.size()!>!0)!x!else!y if*(x!&&!x.size())!x!else!y if*(x)!x!else!y x!?!x!:!y x!?:!y Good old ternary operator 60 Null, empty, zerosized... false, otherwise true!
  128. 128. Towards Elvis... def!(x,!y)!=!['MacBook!Pro',!'unknown'] if*(x!!=!null!&&!x.size()!>!0)!x!else!y if*(x!&&!x.size())!x!else!y if*(x)!x!else!y x!?!x!:!y x!?:!y Elvis! 60 Good old ternary operator Null, empty, zerosized... false, otherwise true!
  129. 129. AST transformations • Abstract Syntax Tree – in memory representation of your program before being compiled into bytecode • AST transformation == process of transforming the AST of a program before it’s compiled • Macro-like compiler hook! 61
  130. 130. Lots of AST transformations... • Code generation – @ToString, @EqualsAndHashCode, @Canonical, @TupleConstructor, @InheritConstructors, @Category, @IndexedProperty, @Lazy, @Newify • Class design – @Delegate, @Immutable, @Memoized, @Singleton, @Mixin • Logging – @Log, @Log4j, @Log4j2, @Slf4j 62
  131. 131. Lots of AST transformations... • Safer scripting – @ConditionalInterrupt, @ThreadInterrupt, @TimedInterupt • Compiler directives – @Field, @PackageScope, @AnnotationCollector, @DelegatesTo, @TypeChecked, @CompileStatic, @CompileDynamic • Swing patterns – @Bindable, @ListenerList, @Vetoable 63
  132. 132. Lots of AST transformations... • Dependencies handling – @Grab, @GrabConfig, @GrabExclude, @GrabResolver • Test assistance – @NotYetImplemented, @ASTTest 64
  133. 133. Immutability • Implement immutability by the book – final class – tuple-style constructor – private final backing fields – defensive copying of collections – equals() and hashCode() methods – toString() method – ... 65
  134. 134. Immutability • Implement immutability by the book – final class Can be error-prone to – tuple-style constructor write immutable – private final backing fields classes oneself! – defensive copying of collections – equals() and hashCode() methods – toString() method – ... 65
  135. 135. Immutability • A Person class with public final class Person { private final String name; private final int age; public Person(String name, int age) { this.name = name; this.age = age; } – a String name – an int age public String getName() { return name; } public int getAge() { return age; } public int hashCode() { return age + 31 * name.hashCode(); } public boolean equals(Object other) { if (other == null) { return false; } if (this == other) { return true; } if (Person.class != other.getClass()) { return false; } Person otherPerson = (Person)other; if (!name.equals(otherPerson.getName()) { return false; } if (age != otherPerson.getAge()) { return false; } return true; } public String toString() { return "Person(" + name + ", " + age + ")"; } } 66
  136. 136. Immutability • A Person class with Damn verbose Java! public final class Person { private final String name; private final int age; public Person(String name, int age) { this.name = name; this.age = age; } – a String name – an int age public String getName() { return name; } public int getAge() { return age; } public int hashCode() { return age + 31 * name.hashCode(); } public boolean equals(Object other) { if (other == null) { return false; } if (this == other) { return true; } if (Person.class != other.getClass()) { return false; } Person otherPerson = (Person)other; if (!name.equals(otherPerson.getName()) { return false; } if (age != otherPerson.getAge()) { return false; } return true; } public String toString() { return "Person(" + name + ", " + age + ")"; } } 66
  137. 137. Immutability • A Person class with Damn verbose Java! public final class Person { private final String name; private final int age; public Person(String name, int age) { this.name = name; this.age = age; } – a String name – an int age public String getName() { return name; } public int getAge() { return age; } public int hashCode() { return age + 31 * name.hashCode(); } public boolean equals(Object other) { if (other == null) { return false; } if (this == other) { return true; } if (Person.class != other.getClass()) { return false; } Person otherPerson = (Person)other; if (!name.equals(otherPerson.getName()) { return false; } if (age != otherPerson.getAge()) { return false; } return true; } Although it’s also a valid Groovy program! public String toString() { return "Person(" + name + ", " + age + ")"; } } 66
  138. 138. @Immutable import!groovy.transform.* @Immutable class!Person!{ !!!!String!name !!!!int!age } 67
  139. 139. Memoization • Cache the result of previous invocations of closures or methods with the same set of argument values import!groovy.transform.* @Memoized long!fib(long!n)!{ !!!!if!(n!==!0)!0 !!!!else!if!(n!==!1)!1 !!!!else!fib(n!c!1)!+!fib(n!c!2) } println!fib(40) 68
  140. 140. Memoization • Cache the result of previous invocations of closures or methods with the same set of argument values import!groovy.transform.* Best applied to side-effect free functions @Memoized long!fib(long!n)!{ !!!!if!(n!==!0)!0 !!!!else!if!(n!==!1)!1 !!!!else!fib(n!c!1)!+!fib(n!c!2) } println!fib(40) 68
  141. 141. Groovy allows you to be lazy
  142. 142. Groovy allows you to be lazy The compiler will do the job for you
  143. 143. Groovy allows you to be lazy More concise, more readable code The compiler will do the job for you
  144. 144. Groovy allows you to be lazy More concise, more readable code The compiler will do the job for you Less stuff to maintain and worry about
  145. 145. @TypeChecked and @CompileStatic • Static type checking with @TypeChecked, throws compilation errors on... – typos in method and variable names – incompatible return types – wrong type assignments • Supports fine-grained type inference – « Least Upper Bound » – « Flow typing » 70
  146. 146. @TypeChecked and @CompileStatic • Static type checking with @TypeChecked, throws compilation errors on... – typos in method and variable names – incompatible return types You can even extend the – wrong type assignments static type checker! • Supports fine-grained type inference – « Least Upper Bound » – « Flow typing » 70
  147. 147. @TypeChecked and @CompileStatic • Static type checking with @TypeChecked, throws compilation errors on... – typos in method and variable names – incompatible return types You can even extend the – wrong type assignments static type checker! • Supports fine-grained type inference – « Least Upper Bound » – « Flow typing » 70 Type check DSLs or dynamic features!
  148. 148. @TypeChecked and @CompileStatic • What is type checked can also be compiled statically with @CompileStatic – generate the same bytecode as javac – same performance as Java 71
  149. 149. Pi (π) Fibonacci quadrature Java 191 ms 97 ms 3.6 s 2.x Static compilation 197 ms 101 ms 4.3 s 1.8 72 Binary trees Primitive optimizations 360 ms 111 ms 23.7 s 1.7 Static compilation performance No prim. optimizations 2590 ms 3220 ms 50.0 s
  150. 150. 3 t r a P Superb community!
  151. 151. A blossoming Ecosystem
  152. 152. M V G
  153. 153. GVM
  154. 154. GVM GROOVY ENVIRONMENT MANAGER
  155. 155. GVM: Groovy enVironment Manager • The new kid on the block – http://gvmtool.net/ — @gvmtool • Manage parallel versions of the various ecosystem projects • Supports... – Groovy, Grails, Griffon, Gradle,Vert.x, Spring Boot • On Linux, MacOS, Cygwin, Solaris, FreeBSD 81
  156. 156. I’m Spock...
  157. 157. I’m Spock... ...the Spock testing framework
  158. 158. I’m Spock... ...the Spock testing framework
  159. 159. Spock example @Grab('org.spockframework:spockccore:0.7cgroovyc2.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!||!4 !!!!!!!!0!|!0!||!0 !!!!} } 83
  160. 160. Spock example @Grab a dependency @Grab('org.spockframework:spockccore:0.7cgroovyc2.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!||!4 !!!!!!!!0!|!0!||!0 !!!!} } 83
  161. 161. Spock example @Grab a dependency @Grab('org.spockframework:spockccore:0.7cgroovyc2.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!||!4 !!!!!!!!0!|!0!||!0 !!!!} } 83 Meaningful test method names
  162. 162. Spock example @Grab a dependency @Grab('org.spockframework:spockccore:0.7cgroovyc2.0') import!spock.lang.* Meaningful test method names class!MathSpec!extends!Specification!{ !!!!def!"maximum!of!two!numbers"()!{ ******expect: !!!!!!!!Math.max(a,!b)!==!c Clever use of labels ******where: !!!!!!!!a!|!b!||!c !!!!!!!!1!|!3!||!3 !!!!!!!!7!|!4!||!4 !!!!!!!!0!|!0!||!0 !!!!} } 83 for BDD style
  163. 163. Spock example @Grab a dependency @Grab('org.spockframework:spockccore:0.7cgroovyc2.0') import!spock.lang.* Meaningful test method names class!MathSpec!extends!Specification!{ !!!!def!"maximum!of!two!numbers"()!{ ******expect: !!!!!!!!Math.max(a,!b)!==!c Clever use of labels ******where: !!!!!!!!a!|!b!||!c !!!!!!!!1!|!3!||!3 !!!!!!!!7!|!4!||!4 !!!!!!!!0!|!0!||!0 !!!!} } 83 for BDD style Expression to be asserted
  164. 164. Spock example @Grab a dependency @Grab('org.spockframework:spockccore:0.7cgroovyc2.0') import!spock.lang.* Meaningful test method names class!MathSpec!extends!Specification!{ !!!!def!"maximum!of!two!numbers"()!{ ******expect: !!!!!!!!Math.max(a,!b)!==!c Clever use of labels ******where: !!!!!!!!a!|!b!||!c !!!!!!!!1!|!3!||!3 !!!!!!!!7!|!4!||!4 !!!!!!!!0!|!0!||!0 !!!!} } 83 for BDD style Cute datadriven tests! Expression to be asserted
  165. 165. @GrabResolver("https://oss.jfrog.org/artifactory/repo") @Grab("org.ratpackcframework:ratpackcgroovy:0.9.0cSNAPSHOT") 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" !!!!} }
  166. 166. @GrabResolver("https://oss.jfrog.org/artifactory/repo") @Grab("org.ratpackcframework:ratpackcgroovy:0.9.0cSNAPSHOT") import!static!org.ratpackframework.groovy.RatpackScript.ratpack import!static!org.ratpackframework.groovy.Template.groovyTemplate ratpack!{ !!!!handlers!{ !!!!!!!!get!{ !!!!!!!!!!!!response.send!"Welcome!" !!!!!!!!} Lightweight Netty-based web app toolkit !!!!!!!!get("date")!{ !!!!!!!!!!!!render!groovyTemplate("date.html") !!!!!!!!} !!!!!!!!assets!"public" !!!!} }
  167. 167. Geb • Browser automation solution • WebDriver + jQuery selectors + Groovy • Handy for – scripting, scraping, automation... – functional / web / acceptance testing • when integrated with JUnit, TestNG or Spock 85
  168. 168. Geb — Example 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" } 86
  169. 169. Geb — Example Drive the browser to this site 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" } 86
  170. 170. Geb — Example Drive the browser to this site import!geb.Browser Browser.drive!{ !!!!go!"http://myapp.com/login" Check the content of the title !!!!assert!$("h1").text()!==!"Please!Login" !!!!$("form.login").with!{ !!!!!!!!username!=!"admin" !!!!!!!!password!=!"password" !!!!!!!!login().click() !!!!} !!!!assert!$("h1").text()!==! !!!!!!!!"Admin!Section" } 86
  171. 171. Geb — Example Drive the browser to this site import!geb.Browser Browser.drive!{ !!!!go!"http://myapp.com/login" Check the content of the title !!!!assert!$("h1").text()!==!"Please!Login" !!!!$("form.login").with!{ !!!!!!!!username!=!"admin" !!!!!!!!password!=!"password" !!!!!!!!login().click() !!!!} !!!!assert!$("h1").text()!==! !!!!!!!!"Admin!Section" } 86 Find & fill in the form
  172. 172. Geb — Example Drive the browser to this site import!geb.Browser Browser.drive!{ !!!!go!"http://myapp.com/login" Check the content of the title !!!!assert!$("h1").text()!==!"Please!Login" !!!!$("form.login").with!{ !!!!!!!!username!=!"admin" !!!!!!!!password!=!"password" !!!!!!!!login().click() !!!!} !!!!assert!$("h1").text()!==! !!!!!!!!"Admin!Section" } 86 Find & fill in the form Submit the form
  173. 173. Geb — Example Drive the browser to this site import!geb.Browser Browser.drive!{ !!!!go!"http://myapp.com/login" Check the content of the title !!!!assert!$("h1").text()!==!"Please!Login" !!!!$("form.login").with!{ !!!!!!!!username!=!"admin" !!!!!!!!password!=!"password" !!!!!!!!login().click() !!!!} !!!!assert!$("h1").text()!==! !!!!!!!!"Admin!Section" } Find & fill in the form Submit the form In the admin section, yeah! 86
  174. 174. Geb — With page objects and Spock import!geb.spock.GebSpec class!GoogleWikipediaSpec!extends!GebSpec!{ !!!!def!"first!result!for!wikipedia!search!should!be!wikipedia"()!{ ********given: !!!!!!!!to!GoogleHomePage ********expect: !!!!!!!!at!GoogleHomePage ********when: !!!!!!!!search.field.value("wikipedia") ********then: !!!!!!!!waitFor!{!at!GoogleResultsPage!} ********and: !!!!!!!!firstResultLink.text()!==!"Wikipedia" ********when: !!!!!!!!firstResultLink.click() ********then: !!!!!!!!waitFor!{!at!WikipediaPage!} !!!!} } 87
  175. 175. Geb — With page objects and Spock import!geb.spock.GebSpec class!GoogleWikipediaSpec!extends!GebSpec!{ With page objects !!!!def!"first!result!for!wikipedia!search!should!be!wikipedia"()!{ ********given: !!!!!!!!to!GoogleHomePage ********expect: !!!!!!!!at!GoogleHomePage ********when: !!!!!!!!search.field.value("wikipedia") ********then: !!!!!!!!waitFor!{!at!GoogleResultsPage!} ********and: !!!!!!!!firstResultLink.text()!==!"Wikipedia" ********when: !!!!!!!!firstResultLink.click() ********then: !!!!!!!!waitFor!{!at!WikipediaPage!} !!!!} } 87
  176. 176. Geb — With page objects and Spock import!geb.spock.GebSpec class!GoogleWikipediaSpec!extends!GebSpec!{ With page objects !!!!def!"first!result!for!wikipedia!search!should!be!wikipedia"()!{ ********given: !!!!!!!!to!GoogleHomePage ********expect: !!!!!!!!at!GoogleHomePage BDD style: given/when/then ********when: !!!!!!!!search.field.value("wikipedia") ********then: !!!!!!!!waitFor!{!at!GoogleResultsPage!} ********and: !!!!!!!!firstResultLink.text()!==!"Wikipedia" ********when: !!!!!!!!firstResultLink.click() ********then: !!!!!!!!waitFor!{!at!WikipediaPage!} !!!!} } 87
  177. 177. Geb — With page objects and Spock import!geb.spock.GebSpec class!GoogleWikipediaSpec!extends!GebSpec!{ With page objects !!!!def!"first!result!for!wikipedia!search!should!be!wikipedia"()!{ ********given: !!!!!!!!to!GoogleHomePage ********expect: !!!!!!!!at!GoogleHomePage ********when: !!!!!!!!search.field.value("wikipedia") BDD style: given/when/then Wait for slow loading pages ********then: !!!!!!!!waitFor!{!at!GoogleResultsPage!} ********and: !!!!!!!!firstResultLink.text()!==!"Wikipedia" ********when: !!!!!!!!firstResultLink.click() ********then: !!!!!!!!waitFor!{!at!WikipediaPage!} !!!!} } 87
  178. 178. 4 t r a P Summary
  179. 179. Java’s best friend • Java derived syntax – Flat learning curve – Easy to learn • But goes beyond Java – Concise, expressive, readable – Fit for Domain-Specific Languages • Seamless & transparent Java integration – Mix and match Groovy and Java classes (joint compil.) – No language barrier to cross 89
  180. 180. Groovy’s nature • Object oriented dynamic language... • But... – as type safe as you want it — static type checking – as fast as you need it — static compilation – as functional as you make it — closures... 90
  181. 181. Groovy use cases • Scripting tasks, build automation • Extension points for customizing/configuring apps • Business languages & Domain-Specific Languages • Full blown apps – for desktop with Griffon – for the web with Grails, Ratpack, Gaelyk – for web reactive programming with reactor 91
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×