b -‐> a + b } ! assert adder(1, 2) == 3 assert adder('a', 'b') == 'ab' Short form of: adder.call(‘a’, ‘b’) Assign a function into a variable Closure parameters
b -‐> a + b } ! assert adder(1, 2) == 3 assert adder('a', 'b') == 'ab' Short form of: adder.call(‘a’, ‘b’) Genericity with duck typing & operator overloading Assign a function into a variable Closure parameters
elements -‐> elements.sum() } ! assert sum(1, 2) == 3 assert sum('a', 'b', 'c') == 'abc' You can specify the type: int... Variable number of arguments
new File('out.txt').withWriter { w -‐> r.eachLine { line -‐> if (line.contains('Groovy')) w << line.toUpperCase() } } } Take care of properly opening / closing resources
"Alibaba" ==~ /.*(ba){2}/ ! def matcher = "Superman" =~ /([A-‐Z][a-‐z]+)man/ assert matcher[0][0] == 'Superman' assert matcher[0][1] == 'Super' ! '75001 Paris'.find(/(\d{5})\s(\w)+/) { match, zip, town -‐> println "The Zip code of ${town} is ${zip}" } Pattern Match Find Nice way to decompose the matched regions
assert 3 / 2 == 1.5 One of the reasons why micro- benchmarks sometimes showed Groovy to be slow... But you can use doubles & floats for performance, with ‘d’ or ‘f’ suffixes or with explicit type
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" }
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
json = new JsonBuilder() json.person { name 'Guillaume' age 36 daughters 'Marion', 'Erine' address { street '1 Main Street' zip 75001 city 'Paris' } } Hierarchical data representation
json = new JsonBuilder() json.person { name 'Guillaume' age 36 daughters 'Marion', 'Erine' address { street '1 Main Street' zip 75001 city 'Paris' } } Hierarchical data representation Closure blocks delimiting the structure
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
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 With Java, you only get an NPE. No idea where it came from!
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 With Java, you only get an NPE. No idea where it came from! Groovy will say: Cannot get property ‘name’ on null object
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
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 o?.line?.item?.name
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 o?.line?.item?.name Safe navigation: will just return null; No NPE
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, zero- sized... false, otherwise true!
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, zero- sized... false, otherwise true! Good old ternary operator
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, zero- sized... false, otherwise true! Good old ternary operator Elvis!
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! !66
– an int age !71 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 + ")";! }! }!
– an int age !71 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 + ")";! }! }! Damn verbose Java!
– an int age !71 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 + ")";! }! }! Damn verbose Java! Although it’s also a valid Groovy program!
or methods with the same set of argument values !73 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)
or methods with the same set of argument values !73 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) Best applied to side-effect free functions
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 » !75 You can even extend the static type checker!
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 » !75 You can even extend the static type checker! Type check DSLs or dynamic features!
Java 191 ms 97 ms 3.6 s Static compilation 197 ms 101 ms 4.3 s Primitive optimizations 360 ms 111 ms 23.7 s No prim. optimizations 2590 ms 3220 ms 50.0 s 1.7 1.8 2.x
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 } } @Grab a dependency Meaningful test method names Clever use of labels for BDD style
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 } } @Grab a dependency Meaningful test method names Clever use of labels for BDD style Expression to be asserted
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 } } @Grab a dependency Meaningful test method names Clever use of labels for BDD style Expression to be asserted Cute data- driven tests!
go "http://myapp.com/login" ! assert $("h1").text() == "Please Login" ! $("form.login").with { username = "admin" password = "password" login().click() } ! assert $("h1").text() == "Admin Section" } Drive the browser to this site Check the content of the title
go "http://myapp.com/login" ! assert $("h1").text() == "Please Login" ! $("form.login").with { username = "admin" password = "password" login().click() } ! assert $("h1").text() == "Admin Section" } Drive the browser to this site Check the content of the title Find & fill in the form
go "http://myapp.com/login" ! assert $("h1").text() == "Please Login" ! $("form.login").with { username = "admin" password = "password" login().click() } ! assert $("h1").text() == "Admin Section" } Drive the browser to this site Check the content of the title Find & fill in the form Submit the form
go "http://myapp.com/login" ! assert $("h1").text() == "Please Login" ! $("form.login").with { username = "admin" password = "password" login().click() } ! assert $("h1").text() == "Admin Section" } Drive the browser to this site Check the content of the title Find & fill in the form Submit the form In the admin section, yeah!
! 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 } } } With page objects
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... !95
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 !96