14. Show users live
updates as they come
A « push » approach vs a
« pull » approach
Handle a big
volume of events
15. Show users live
updates as they come
A « push » approach vs a
« pull » approach
Handle a big
volume of events
React fast to
new events
16. Show users live
updates as they come
A « push » approach vs a
« pull » approach
Handle a big
volume of events
React fast to
new events
Use promises, events,
streams to react fast and
stream updates to users as
they are ready
17. Show users live
updates as they come
Avoid blocking
and state
A « push » approach vs a
« pull » approach
Handle a big
volume of events
React fast to
new events
Use promises, events,
streams to react fast and
stream updates to users as
they are ready
18. Show users live
updates as they come
Fail gracefully
Avoid blocking
and state
A « push » approach vs a
« pull » approach
Handle a big
volume of events
React fast to
new events
Use promises, events,
streams to react fast and
stream updates to users as
they are ready
45. Speaking of conciseness...
A full Spring app in the span of a tweet!
@RestController
class
App
{
@RequestMapping("/")
String
home()
{
"Hello
World!"
}
}
57. Scripts versus Classes
public
class
Main
{
public
static
void
main(String[]
args)
{
System.out.println("Hello");
}
}
vs
@glaforge — @smaldini / #DV13-rtweb
!26
58. Scripts versus Classes
public
class
Main
{
public
static
void
main(String[]
args)
{
System.out.println("Hello");
}
}
vs
println
"Hello"
@glaforge — @smaldini / #DV13-rtweb
!26
87. 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")
@glaforge — @smaldini / #DV13-rtweb
!39
88. Optional...
class
Greeter
{
String
owner
!
String
greet(String
name)
{
"Hello
${name},
I
am
${owner}"
}
}
!
def
greeter
=
new
Greeter(owner:
"Guillaume")
!
println
greeter.greet("Marion")
@glaforge — @smaldini / #DV13-rtweb
!40
89. 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"));
@glaforge — @smaldini / #DV13-rtweb
!40
90. Optional...
class
Greeter
{
String
owner
!
String
greet(String
name)
{
"Hello
${name},
I
am
${owner}"
}
}
!
def
greeter
=
new
Greeter(owner:
"Guillaume")
!
println
greeter.greet("Marion")
@glaforge — @smaldini / #DV13-rtweb
!40
91. Native syntax constructs
//
closures
def
adder
=
{
a,
b
-‐>
a
+
b
}
!
//
lists
def
list
=
[1,
2,
3,
4,
5]
!
//
maps
def
map
=
[a:
1,
b:
2,
c:
3]
!
//
regular
expressions
def
regex
=
~/.*foo.*/
!
//
ranges
def
range
128..255
@glaforge — @smaldini / #DV13-rtweb
!41
92. Closures — the basics
• Functions as first-class citizen of the language
def
adder
=
{
a,
b
-‐>
a
+
b
}
!
assert
adder(1,
2)
==
3
assert
adder('a',
'b')
==
'ab'
@glaforge — @smaldini / #DV13-rtweb
!42
93. Closures — the basics
• Functions as first-class citizen of the language
Closure
parameters
def
adder
=
{
a,
b
-‐>
a
+
b
}
!
assert
adder(1,
2)
==
3
assert
adder('a',
'b')
==
'ab'
@glaforge — @smaldini / #DV13-rtweb
!42
94. Closures — the basics
• Functions as first-class citizen of the language
Assign a function
Closure
into a variable
parameters
def
adder
=
{
a,
b
-‐>
a
+
b
}
!
assert
adder(1,
2)
==
3
assert
adder('a',
'b')
==
'ab'
@glaforge — @smaldini / #DV13-rtweb
!42
95. Closures — the basics
• Functions as first-class citizen of the language
Assign a function
Closure
into a variable
parameters
def
adder
=
{
a,
b
-‐>
a
+
b
}
!
assert
adder(1,
2)
==
3
assert
adder('a',
'b')
==
'ab'
Short form of:
adder.call(‘a’, ‘b’)
@glaforge — @smaldini / #DV13-rtweb
!42
96. Closures — the basics
• Functions as first-class citizen of the language
Assign a function
Closure
into a variable
parameters
def
adder
=
{
a,
b
-‐>
a
+
b
}
!
assert
adder(1,
2)
==
3
assert
adder('a',
'b')
==
'ab'
Short form of:
adder.call(‘a’, ‘b’)
@glaforge — @smaldini / #DV13-rtweb
Genericity with
duck typing & operator
overloading
!42
97. Closures — explicit type
!
def
intAdder
=
{
int
a,
int
b
-‐>
a
+
b
}
@glaforge — @smaldini / #DV13-rtweb
!43
98. Closures — explicit type
Be explicit about
the types
!
def
intAdder
=
{
int
a,
int
b
-‐>
a
+
b
}
@glaforge — @smaldini / #DV13-rtweb
!43
103. Closures — variable arguments
Variable number of
arguments
def
sum
=
{
...
elements
-‐>
elements.sum()
}
!
assert
sum(1,
2)
==
3
assert
sum('a',
'b',
'c')
==
'abc'
@glaforge — @smaldini / #DV13-rtweb
!45
104. Closures — variable arguments
You can specify the
type: int...
Variable number of
arguments
def
sum
=
{
...
elements
-‐>
elements.sum()
}
!
assert
sum(1,
2)
==
3
assert
sum('a',
'b',
'c')
==
'abc'
@glaforge — @smaldini / #DV13-rtweb
!45
105. Closures — default values
def
mult
=
{
int
a,
int
b
=
10
-‐>
a
*
b
}
!
assert
mult(2,
3)
==
6
assert
mult(5)
==
50
@glaforge — @smaldini / #DV13-rtweb
!46
106. Closures — default values
Default value
def
mult
=
{
int
a,
int
b
=
10
-‐>
a
*
b
}
!
assert
mult(2,
3)
==
6
assert
mult(5)
==
50
@glaforge — @smaldini / #DV13-rtweb
!46
107. Closures — default values
Default value
def
mult
=
{
int
a,
int
b
=
10
-‐>
a
*
b
}
!
assert
mult(2,
3)
==
6
assert
mult(5)
==
50
@glaforge — @smaldini / #DV13-rtweb
Provided value
for b
!46
108. Closures — default values
Default value
def
mult
=
{
int
a,
int
b
=
10
-‐>
a
*
b
}
!
assert
mult(2,
3)
==
6
assert
mult(5)
==
50
Default value
used for b
@glaforge — @smaldini / #DV13-rtweb
Provided value
for b
!46
112. Closures — map / filter / reduce
@groovy.transform.Immutable
class
Person
{
String
name
int
age
}
@glaforge — @smaldini / #DV13-rtweb
!48
113. Closures — map / filter / reduce
@groovy.transform.Immutable
class
Person
{
String
name
int
age
}
@glaforge — @smaldini / #DV13-rtweb
!48
114. Closures — map / filter / reduce
@groovy.transform.Immutable
class
Person
{
String
name
int
age
}
def
persons
=
[
new
Person('Guillaume',
36),
new
Person('Marion',
5),
new
Person('Erine',
1)
]
@glaforge — @smaldini / #DV13-rtweb
!48
115. Closures — map / filter / reduce
@groovy.transform.Immutable
class
Person
{
String
name
int
age
}
def
persons
=
[
new
Person('Guillaume',
36),
new
Person('Marion',
5),
new
Person('Erine',
1)
]
@glaforge — @smaldini / #DV13-rtweb
!48
116. Closures — map / filter / reduce
@groovy.transform.Immutable
class
Person
{
String
name
int
age
}
def
persons
=
[
new
Person('Guillaume',
36),
new
Person('Marion',
5),
new
Person('Erine',
1)
]
def
names
=
persons.findAll
{
it.age
<
18
}
.collect
{
it.name.toUpperCase()
}
.sort()
.join(',
')
@glaforge — @smaldini / #DV13-rtweb
!48
117. Closures — map / filter / reduce
@groovy.transform.Immutable
class
Person
{
String
name
int
age
}
def
persons
=
[
new
Person('Guillaume',
36),
new
Person('Marion',
5),
new
Person('Erine',
1)
]
def
names
=
persons.findAll
{
it.age
<
18
}
.collect
{
it.name.toUpperCase()
}
.sort()
.join(',
')
@glaforge — @smaldini / #DV13-rtweb
!48
118. Closures — map / filter / reduce
@groovy.transform.Immutable
class
Person
{
String
name
int
age
}
def
persons
=
[
new
Person('Guillaume',
36),
new
Person('Marion',
5),
new
Person('Erine',
1)
]
def
names
=
persons.findAll
{
it.age
<
18
}
.collect
{
it.name.toUpperCase()
}
.sort()
.join(',
')
assert
names
==
"ERINE,
MARION"
@glaforge — @smaldini / #DV13-rtweb
!48
119. Closures — map / filter / reduce
@groovy.transform.Immutable
class
Person
{
String
name
int
age
}
def
persons
=
[
new
Person('Guillaume',
36),
new
Person('Marion',
5),
new
Person('Erine',
1)
]
find/findAll, inject, collect, flatten,
min/max, unique, reverse, collate,
groupBy, any/every, head/tail/last,
count/countBy, combinations/
permutations/subsequences/
transpose, withDefault/
withLazyDefault
def
names
=
persons.findAll
{
it.age
<
18
}
.collect
{
it.name.toUpperCase()
}
.sort()
.join(',
')
assert
names
==
"ERINE,
MARION"
@glaforge — @smaldini / #DV13-rtweb
!48
120. Closures — resource handling
new
File('bible.txt').withReader
{
r
-‐>
new
File('out.txt').withWriter
{
w
-‐>
r.eachLine
{
line
-‐>
if
(line.contains('Groovy'))
w
<<
line.toUpperCase()
}
}
}
@glaforge — @smaldini / #DV13-rtweb
!49
121. Closures — resource handling
Take care of properly opening /
closing resources
new
File('bible.txt').withReader
{
r
-‐>
new
File('out.txt').withWriter
{
w
-‐>
r.eachLine
{
line
-‐>
if
(line.contains('Groovy'))
w
<<
line.toUpperCase()
}
}
}
@glaforge — @smaldini / #DV13-rtweb
!49
123. Closures — custom control structures
Closure as last argument
void
unless(boolean
cond,
Closure
c)
{
if
(!cond)
c()
}
!
unless
(10
<
9)
{
println
"less"
}
@glaforge — @smaldini / #DV13-rtweb
!50
124. Closures — custom control structures
Closure as last argument
void
unless(boolean
cond,
Closure
c)
{
if
(!cond)
c()
}
!
unless
(10
<
9)
{
println
"less"
}
@glaforge — @smaldini / #DV13-rtweb
Equivalent to:
unless(10<9, {...})
!50
125. Closures — custom control structures
class
Reactor
{
void
on(Selector
s,
Closure
c)
{
…
}
}
…
reactor.on(
$(‘abc')
)
{
Event<String>
ev
-‐>
…
}
@glaforge — @smaldini / #DV13-rtweb
!51
126. Closures — builders leverage closures
import
groovy.json.*
!
def
json
=
new
JsonBuilder()
json.person
{
name
'Guillaume'
age
36
daughters
'Marion',
'Erine'
address
{
street
'1
Main
Street'
zip
75001
city
'Paris'
}
}
@glaforge — @smaldini / #DV13-rtweb
!52
127. Closures — builders leverage closures
import
groovy.json.*
!
def
json
=
new
JsonBuilder()
json.person
{
name
'Guillaume'
age
36
daughters
'Marion',
'Erine'
address
{
street
'1
Main
Street'
zip
75001
city
'Paris'
}
}
@glaforge — @smaldini / #DV13-rtweb
Hierarchical data
representation
!52
128. Closures — builders leverage closures
import
groovy.json.*
!
Hierarchical data
representation
def
json
=
new
JsonBuilder()
json.person
{
name
'Guillaume'
age
36
Closure blocks
daughters
'Marion',
'Erine'
delimiting the
address
{
structure
street
'1
Main
Street'
zip
75001
city
'Paris'
}
}
@glaforge — @smaldini / #DV13-rtweb
!52
129. Closures — builders leverage closures
import
groovy.json.*
{
"person":
{
Hierarchical data
def
json
=
new
JsonBuilder()
"name":
"Guillaume",
representation
"age":
36,
json.person
{
"daughters":
[
name
'Guillaume'
"Marion",
"Erine"
age
36
],
Closure blocks
daughters
'Marion',
'Erine'
"address":
{
delimiting the
"street":
"1
Main
Street",
address
{
"zip":
75001,
structure
street
'1
Main
Street'
"city":
"Paris"
}
zip
75001
}
city
'Paris'
}
!
}
}
@glaforge — @smaldini / #DV13-rtweb
!52
130. Closure — your own builders
GroovyEnvironment.create
{
environment
{
defaultDispatcher
=
"test"
!
dispatcher('test')
{
type
=
DispatcherType.SYNCHRONOUS
}
}
}
@glaforge — @smaldini / #DV13-rtweb
!53
131. Closure — your own builders
GroovyEnvironment.create
{
environment
{
defaultDispatcher
=
"test"
!
dispatcher('test')
{
type
=
DispatcherType.SYNCHRONOUS
}
}
}
@glaforge — @smaldini / #DV13-rtweb
!53
132. Closure — your own builders
GroovyEnvironment.create
{
environment
{
defaultDispatcher
=
"test"
!
dispatcher('test')
{
type
=
DispatcherType.SYNCHRONOUS
}
}
}
@glaforge — @smaldini / #DV13-rtweb
!53
133. Closure — your own builders
GroovyEnvironment.create
{
environment
{
defaultDispatcher
=
"test"
!
dispatcher('test')
{
type
=
DispatcherType.SYNCHRONOUS
}
}
}
@glaforge — @smaldini / #DV13-rtweb
!53
134. Closure — your own builders
GroovyEnvironment.create
{
environment
{
defaultDispatcher
=
"test"
!
dispatcher('test')
{
type
=
DispatcherType.SYNCHRONOUS
}
}
}
@glaforge — @smaldini / #DV13-rtweb
!53
135. Closure — your own builders
Implement a builder extending
BuilderSupport or FactoryBuilderSupport…
GroovyEnvironment.create
{
or roll your own!
environment
{
defaultDispatcher
=
"test"
!
dispatcher('test')
{
type
=
DispatcherType.SYNCHRONOUS
}
}
}
@glaforge — @smaldini / #DV13-rtweb
!53
136. Closure — your own builders
Implement a builder extending
BuilderSupport or FactoryBuilderSupport…
GroovyEnvironment.create
{
or roll your own!
environment
{
defaultDispatcher
=
"test"
abstract
class
BuilderSupport
{
abstract
void
setParent(Object
parent,
Object
child)
!
abstract
Object
createNode(Object
name)
dispatcher('test')
{
abstract
Object
createNode(Object
name,
Object
value)
abstract
Object
createNode(Object
name,
Map
attributes)
type
=
DispatcherType.SYNCHRONOUS
abstract
Object
createNode(Object
name,
Map
attributes,
Object
value)
}
}
}
}
@glaforge — @smaldini / #DV13-rtweb
!53
137. GPath expressions
• GPath expressions are like XPath
but for an object graph
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'
@glaforge — @smaldini / #DV13-rtweb
!54
138. GPath expressions
• GPath expressions are like XPath
but for an object graph
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'
GPath expression
@glaforge — @smaldini / #DV13-rtweb
!54
139. GPath expressions
• GPath expressions are like XPath
but for an object graph
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'
GPath expression
@glaforge — @smaldini / #DV13-rtweb
Add find / findAll
into the mix
!54
140. GPath expressions
• GPath expressions are like XPath
but for an object graph
No (un)marshalling!
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'
GPath expression
@glaforge — @smaldini / #DV13-rtweb
Add find / findAll
into the mix
!54
141. 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
-‐>
a
+
b
}
==
'abcd'
@glaforge — @smaldini / #DV13-rtweb
!55
142. 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
-‐>
a
+
b
}
==
'abcd'
@glaforge — @smaldini / #DV13-rtweb
!55
143. Lists
List definition
def
list
=
['a',
'b',
'c']
!
list
<<
'd'
assert
list.contains('d')
Append an element
(operator overloading)
!
assert
list.findAll
{
it.startsWith
'a'
}.size()
==
1
assert
list.collect
{
it.toUpperCase()
}
==
['A',
'B',
'C',
'D']
assert
list.inject('')
{
a,
b
-‐>
a
+
b
}
==
'abcd'
@glaforge — @smaldini / #DV13-rtweb
!55
144. Lists
List definition
def
list
=
['a',
'b',
'c']
!
list
<<
'd'
assert
list.contains('d')
Append an element
(operator overloading)
!
assert
list.findAll
{
it.startsWith
'a'
}.size()
==
1
assert
list.collect
{
it.toUpperCase()
}
==
['A',
'B',
'C',
'D']
assert
list.inject('')
{
a,
b
-‐>
a
+
b
}
==
'abcd'
Functional-style map /
filter / reduce with
closures
@glaforge — @smaldini / #DV13-rtweb
!55
169. BigDecimal by default!
assert
2.0
-‐
1.1
==
0.9
assert
3
/
2
==
1.5
One of the reasons why microbenchmarks sometimes showed
Groovy to be slow...
@glaforge — @smaldini / #DV13-rtweb
!62
170. BigDecimal by default!
assert
2.0
-‐
1.1
==
0.9
assert
3
/
2
==
1.5
One of the reasons why microbenchmarks sometimes showed
Groovy to be slow...
@glaforge — @smaldini / #DV13-rtweb
But you can use doubles & floats for
performance, with ‘d’ or ‘f ’ suffixes
or with explicit type
!62
173. 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]:
"in
list";
break
case
~/.*o+.*/:
"regex
match";
break
case
{
it
<
3
}:
"closure
criteria";
break
default:
"unknown"
}
176. Named arguments
move
obj,
x:
3,
y:
4
Normal argument
@glaforge — @smaldini / #DV13-rtweb
Named argument
!64
177. Named arguments
move
obj,
x:
3,
y:
4
Normal argument
Named argument
Calls:
move(Map m, Object)
@glaforge — @smaldini / #DV13-rtweb
!64
178. Command chains
• Ability to chain method calls without parentheses and dots
move
forward
at
3.km/h
@glaforge — @smaldini / #DV13-rtweb
!65
179. 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))
@glaforge — @smaldini / #DV13-rtweb
!65
180. Named arguments & command chains
check
that:
vodka
tastes
good
@glaforge — @smaldini / #DV13-rtweb
!66
181. Named arguments & command chains
check
that:
vodka
tastes
good
Will call:
check(that: vodka).tastes(good)
@glaforge — @smaldini / #DV13-rtweb
!66
182. 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
@glaforge — @smaldini / #DV13-rtweb
!67
183. 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
@glaforge — @smaldini / #DV13-rtweb
!67
184. 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
@glaforge — @smaldini / #DV13-rtweb
!67
185. 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
@glaforge — @smaldini / #DV13-rtweb
!67
186. 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
@glaforge — @smaldini / #DV13-rtweb
!67
187. 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
@glaforge — @smaldini / #DV13-rtweb
!68
188. 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
@glaforge — @smaldini / #DV13-rtweb
!68
189. 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
@glaforge — @smaldini / #DV13-rtweb
!68
190. Operator overloading
import
static
Unit.*
!
enum
Unit
{
M(1),
KM(1000)
!
double
mult
Unit(double
mult)
{
this.mult
=
mult
}
}
@glaforge — @smaldini / #DV13-rtweb
!69
191. Operator overloading
import
static
Unit.*
!
enum
Unit
{
M(1),
KM(1000)
!
import
groovy.transform.Immutable
import
static
Unit.*
double
mult
Unit(double
mult)
{
!
this.mult
=
mult
@Immutable
}
class
Distance
{
}
double
q
Unit
u
!
Distance
plus(Distance
d)
{
new
Distance(
q
*
u.mult
+
d.q
*
d.u.mult,
M)
}
}
@glaforge — @smaldini / #DV13-rtweb
!69
192. Operator overloading
import
static
Unit.*
!
import
static
Unit.*
enum
Unit
{
M(1),
KM(1000)
!
def
d10
=
new
Distance(10,
M)
!
import
groovy.transform.Immutable
def
d15
=
new
Distance(15,
M)
double
mult
import
static
Unit.*
!
Unit(double
mult)
{
!
def
d25
=
d10
+
d15
this.mult
=
mult
@Immutable
!
}
class
Distance
{
assert
d25.q
==
25
&&
d25.u
==
M
}
double
q
Unit
u
!
Distance
plus(Distance
d)
{
new
Distance(
q
*
u.mult
+
d.q
*
d.u.mult,
M)
}
}
@glaforge — @smaldini / #DV13-rtweb
!69
193. Operator overloading
import
static
Unit.*
!
import
static
Unit.*
enum
Unit
{
M(1),
KM(1000)
!
def
d10
=
new
Distance(10,
M)
!
import
groovy.transform.Immutable
def
d15
=
new
Distance(15,
M)
double
mult
import
static
Unit.*
!
Unit(double
mult)
{
!
def
d25
=
d10
+
d15
this.mult
=
mult
@Immutable
!
}
class
Distance
{
assert
d25.q
==
25
&&
d25.u
==
M
}
double
q
Unit
u
Overload the ‘+’
operator
@glaforge — @smaldini / #DV13-rtweb
!
Distance
plus(Distance
d)
{
new
Distance(
q
*
u.mult
+
d.q
*
d.u.mult,
M)
}
}
!69
194. Operator overloading
import
static
Unit.*
!
import
static
Unit.*
enum
Unit
{
M(1),
KM(1000)
!
def
d10
=
new
Distance(10,
M)
!
import
groovy.transform.Immutable
def
d15
=
new
Distance(15,
M)
double
mult
import
static
Unit.*
!
Unit(double
mult)
{
!
def
d25
=
d10
+
d15
this.mult
=
mult
@Immutable
!
}
class
Distance
{
assert
d25.q
==
25
&&
d25.u
==
M
}
double
q
Unit
u
Overload the ‘+’
operator
@glaforge — @smaldini / #DV13-rtweb
!
Distance
plus(Distance
d)
{
new
Distance(
q
*
u.mult
+
d.q
*
d.u.mult,
M)
}
}
Use ‘+’
!69
195. Operator overloading
a
+
b
//
a.plus(b)
a
-‐
b
//
a.minus(b)
a
*
b
//
a.multiply(b)
a
/
b
//
a.div(b)
a
%
b
//
a.modulo(b)
a
**
b
//
a.power(b)
a
|
b
//
a.or(b)
a
&
b
//
a.and(b)
a
^
b
//
a.xor(b)
a[b]
//
a.getAt(b)
a
<<
b
//
a.leftShift(b)
a
>>
b
//
a.rightShift(b)
a
>>>
b
//
a.rightShiftUnsigned(b)
+a
//
a.unaryPlus()
-‐a
//
a.unaryMinus()
~a
//
a.bitwiseNegate()
@glaforge — @smaldini / #DV13-rtweb
!70
196. Operator overloading
a
+
b
//
a.plus(b)
a
-‐
b
//
a.minus(b)
a
*
b
//
a.multiply(b)
Operator overloading is
a
/
b
//
a.div(b)
a
%
b
//
a.modulo(b)
just a convention on
a
**
b
//
a.power(b)
method names
a
|
b
//
a.or(b)
a
&
b
//
a.and(b)
a
^
b
//
a.xor(b)
a[b]
//
a.getAt(b)
a
<<
b
//
a.leftShift(b)
a
>>
b
//
a.rightShift(b)
a
>>>
b
//
a.rightShiftUnsigned(b)
+a
//
a.unaryPlus()
-‐a
//
a.unaryMinus()
~a
//
a.bitwiseNegate()
@glaforge — @smaldini / #DV13-rtweb
!70
197. Power asserts
def
(a,
b,
c)
=
[20,
30,
40]
!
assert
a
*
(b
-‐
1)
/
10
==
3
*
c
/
2
+
1
@glaforge — @smaldini / #DV13-rtweb
!71
199. Power asserts
def
(a,
b,
c)
=
[20,
30,
40]
!
assert
a
*
(b
-‐
1)
/
10
==
3
*
c
/
2
+
1
Assertion
failed:
!
assert
a
*
(b
-‐
1)
/
10
==
3
*
c
/
2
+
1
|
|
|
|
|
|
|
|
|
|
|
580|
29
58
false|
|
60
61
20
30
|
40
120
!
Invented by the Spock testing framework
at
script1.run(script1.groovy:3)
@glaforge — @smaldini / #DV13-rtweb
!71
200. Null handling
class
Order
{
LineItem
line
}
class
LineItem
{
int
quantity
Item
item
}
class
Item
{
String
name
}
!
def
o
=
new
Order(
line:
new
LineItem(
quantity:
2,
item:
null))
!
println
o.line.item.name
@glaforge — @smaldini / #DV13-rtweb
!72
201. Null handling
class
Order
{
LineItem
line
}
class
LineItem
{
int
quantity
Item
item
}
class
Item
{
String
name
}
!
def
o
=
new
Order(
line:
new
LineItem(
quantity:
2,
item:
null))
With Java, you only get an NPE.
No idea where it came from!
!
println
o.line.item.name
@glaforge — @smaldini / #DV13-rtweb
!72
202. Null handling
class
Order
{
LineItem
line
}
class
LineItem
{
int
quantity
Item
item
}
class
Item
{
String
name
}
!
def
o
=
new
Order(
line:
new
LineItem(
quantity:
2,
item:
null))
!
println
o.line.item.name
@glaforge — @smaldini / #DV13-rtweb
With Java, you only get an NPE.
No idea where it came from!
Groovy will say:
Cannot get property ‘name’ on null object
!72
203. Null handling
class
Order
{
LineItem
line
}
class
LineItem
{
int
quantity
Item
item
}
class
Item
{
String
name
}
!
def
o
=
new
Order(
line:
new
LineItem(
quantity:
2,
item:
null))
!
println
o.line.item.name
@glaforge — @smaldini / #DV13-rtweb
!72
204. Null handling
class
Order
{
LineItem
line
}
class
LineItem
{
int
quantity
Item
item
}
class
Item
{
String
name
}
o?.line?.item?.name
!
def
o
=
new
Order(
line:
new
LineItem(
quantity:
2,
item:
null))
!
println
o.line.item.name
@glaforge — @smaldini / #DV13-rtweb
!72
205. Null handling
class
Order
{
LineItem
line
}
class
LineItem
{
int
quantity
Item
item
}
class
Item
{
String
name
}
o?.line?.item?.name
Safe navigation:
will just return
null; No NPE
!
def
o
=
new
Order(
line:
new
LineItem(
quantity:
2,
item:
null))
!
println
o.line.item.name
@glaforge — @smaldini / #DV13-rtweb
!72
219. Towards Elvis...
def
(x,
y)
=
['MacBook
Pro',
'unknown']
if
(x
!=
null
&&
x.size()
>
0)
x
else
y
@glaforge — @smaldini / #DV13-rtweb
!77
220. Towards Elvis...
def
(x,
y)
=
['MacBook
Pro',
'unknown']
if
(x
!=
null
&&
x.size()
>
0)
x
else
y
if
(x
&&
x.size())
x
else
y
@glaforge — @smaldini / #DV13-rtweb
!77
221. 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
@glaforge — @smaldini / #DV13-rtweb
!77
222. 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
@glaforge — @smaldini / #DV13-rtweb
!77
223. 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
@glaforge — @smaldini / #DV13-rtweb
!77
224. 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!
@glaforge — @smaldini / #DV13-rtweb
!77
225. 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
@glaforge — @smaldini / #DV13-rtweb
Null, empty, zerosized... false,
otherwise true!
!77
226. 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!
@glaforge — @smaldini / #DV13-rtweb
Good old ternary
operator
Null, empty, zerosized... false,
otherwise true!
!77
227. 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!
@glaforge — @smaldini / #DV13-rtweb
!78
231. 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
– ...
@glaforge — @smaldini / #DV13-rtweb
!82
232. Immutability
• Implement immutability
by the book
!
Can be error-prone to
write immutable
classes oneself!
– final class
– tuple-style constructor
– private final backing fields
– defensive copying of collections
– equals() and hashCode() methods
– toString() method
– ...
@glaforge — @smaldini / #DV13-rtweb
!82
233. Immutability
• A Person class with
– a String name
– an int age
public final class Person {!
private final String name;!
private final int age;!
!
!
!
!
!
!
public Person(String name, int age) {!
this.name = name;!
this.age = 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 + ")";!
}!
}!
@glaforge — @smaldini / #DV13-rtweb
!83
234. Immutability
• A Person class with
– a String name
– an int age
public final class Person {!
private final String name;!
private final int age;!
!
!
!
!
!
!
public Person(String name, int age) {!
this.name = name;!
this.age = age;!
}!
Damn
verbose
Java!
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 + ")";!
}!
}!
@glaforge — @smaldini / #DV13-rtweb
!83
235. Immutability
• A Person class with
– a String name
– an int age
public final class Person {!
private final String name;!
private final int age;!
!
!
!
!
!
!
public Person(String name, int age) {!
this.name = name;!
this.age = age;!
}!
Damn
verbose
Java!
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 + ")";!
}!
}!
@glaforge — @smaldini / #DV13-rtweb
!83
237. 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
-‐
1)
+
fib(n
-‐
2)
}
!
println
fib(40)
@glaforge — @smaldini / #DV13-rtweb
!85
238. 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
-‐
1)
+
fib(n
-‐
2)
}
!
println
fib(40)
@glaforge — @smaldini / #DV13-rtweb
!85
242. Groovy allows you
to be lazy
More concise, more
readable code
The compiler will do
the job for you
243. 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
244. @TypeChecked & @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 »
@glaforge — @smaldini / #DV13-rtweb
!87
245. @TypeChecked & @CompileStatic
• Static type checking with @TypeChecked, throws
compilation errors on...
– typos in method and variable names
– incompatible return types
– wrong type assignments
!
You can even extend the
static type checker!
• Supports fine-grained type inference
– « Least Upper Bound »
– « Flow typing »
@glaforge — @smaldini / #DV13-rtweb
!87
246. @TypeChecked & @CompileStatic
• Static type checking with @TypeChecked, throws
compilation errors on...
– typos in method and variable names
– incompatible return types
– wrong type assignments
!
You can even extend the
static type checker!
• Supports fine-grained type inference
– « Least Upper Bound »
– « Flow typing »
@glaforge — @smaldini / #DV13-rtweb
Type check DSLs or
dynamic features!
!87