2013-08-17 19:12:25 +04:00
|
|
|
---
|
|
|
|
language: Groovy
|
|
|
|
filename: learngroovy.groovy
|
|
|
|
contributors:
|
2013-08-17 21:44:19 +04:00
|
|
|
- ["Roberto Pérez Alcolea", "http://github.com/rpalcolea"]
|
2013-08-17 19:12:25 +04:00
|
|
|
filename: learngroovy.groovy
|
|
|
|
---
|
|
|
|
|
2024-06-03 22:31:20 +03:00
|
|
|
[Groovy](http://www.groovy-lang.org/) is a dynamic language for the Java platform
|
2013-08-17 19:19:47 +04:00
|
|
|
|
2013-09-02 15:53:05 +04:00
|
|
|
```groovy
|
2013-08-17 20:23:36 +04:00
|
|
|
/*
|
|
|
|
Set yourself up:
|
|
|
|
|
2015-12-11 20:01:23 +03:00
|
|
|
1) Install SDKMAN - http://sdkman.io/
|
|
|
|
2) Install Groovy: sdk install groovy
|
2013-08-17 20:23:36 +04:00
|
|
|
3) Start the groovy console by typing: groovyConsole
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
// Single line comments start with two forward slashes
|
|
|
|
/*
|
|
|
|
Multi line comments look like this.
|
|
|
|
*/
|
|
|
|
|
|
|
|
// Hello World
|
|
|
|
println "Hello world!"
|
|
|
|
|
|
|
|
/*
|
|
|
|
Variables:
|
|
|
|
|
|
|
|
You can assign values to variables for later use
|
|
|
|
*/
|
|
|
|
|
|
|
|
def x = 1
|
|
|
|
println x
|
|
|
|
|
|
|
|
x = new java.util.Date()
|
|
|
|
println x
|
|
|
|
|
|
|
|
x = -3.1499392
|
|
|
|
println x
|
|
|
|
|
|
|
|
x = false
|
|
|
|
println x
|
|
|
|
|
|
|
|
x = "Groovy!"
|
|
|
|
println x
|
|
|
|
|
|
|
|
/*
|
|
|
|
Collections and maps
|
|
|
|
*/
|
2013-09-02 15:59:18 +04:00
|
|
|
|
2013-08-17 20:23:36 +04:00
|
|
|
//Creating an empty list
|
|
|
|
def technologies = []
|
|
|
|
|
2023-04-15 10:22:38 +03:00
|
|
|
// or create a list with data
|
|
|
|
technologies = ["Kotlin", "Swift"]
|
|
|
|
|
2013-09-02 15:57:29 +04:00
|
|
|
/*** Adding a elements to the list ***/
|
|
|
|
|
|
|
|
// As with Java
|
2013-08-17 20:23:36 +04:00
|
|
|
technologies.add("Grails")
|
2013-09-02 15:57:29 +04:00
|
|
|
|
|
|
|
// Left shift adds, and returns the list
|
|
|
|
technologies << "Groovy"
|
|
|
|
|
|
|
|
// Add multiple elements
|
2013-08-17 20:23:36 +04:00
|
|
|
technologies.addAll(["Gradle","Griffon"])
|
|
|
|
|
2013-09-02 15:57:29 +04:00
|
|
|
/*** Removing elements from the list ***/
|
|
|
|
|
|
|
|
// As with Java
|
2013-08-17 20:23:36 +04:00
|
|
|
technologies.remove("Griffon")
|
|
|
|
|
2013-09-02 15:57:29 +04:00
|
|
|
// Subtraction works also
|
|
|
|
technologies = technologies - 'Grails'
|
|
|
|
|
2013-09-02 16:02:53 +04:00
|
|
|
/*** Iterating Lists ***/
|
|
|
|
|
2013-09-02 15:57:29 +04:00
|
|
|
// Iterate over elements of a list
|
2013-08-17 20:23:36 +04:00
|
|
|
technologies.each { println "Technology: $it"}
|
|
|
|
technologies.eachWithIndex { it, i -> println "$i: $it"}
|
|
|
|
|
2013-09-02 16:02:53 +04:00
|
|
|
/*** Checking List contents ***/
|
|
|
|
|
2013-08-17 20:23:36 +04:00
|
|
|
//Evaluate if a list contains element(s) (boolean)
|
2013-09-02 16:02:53 +04:00
|
|
|
contained = technologies.contains( 'Groovy' )
|
|
|
|
|
|
|
|
// Or
|
|
|
|
contained = 'Groovy' in technologies
|
|
|
|
|
|
|
|
// Check for multiple contents
|
2013-08-17 20:23:36 +04:00
|
|
|
technologies.containsAll(['Groovy','Grails'])
|
|
|
|
|
2013-09-02 16:02:53 +04:00
|
|
|
/*** Sorting Lists ***/
|
|
|
|
|
2013-09-02 15:59:18 +04:00
|
|
|
// Sort a list (mutates original list)
|
2013-08-17 20:23:36 +04:00
|
|
|
technologies.sort()
|
|
|
|
|
2013-09-02 15:59:18 +04:00
|
|
|
// To sort without mutating original, you can do:
|
|
|
|
sortedTechnologies = technologies.sort( false )
|
|
|
|
|
2016-05-27 12:30:17 +03:00
|
|
|
/*** Manipulating Lists ***/
|
2013-09-02 16:02:53 +04:00
|
|
|
|
2013-08-17 20:23:36 +04:00
|
|
|
//Replace all elements in the list
|
|
|
|
Collections.replaceAll(technologies, 'Gradle', 'gradle')
|
|
|
|
|
|
|
|
//Shuffle a list
|
|
|
|
Collections.shuffle(technologies, new Random())
|
|
|
|
|
|
|
|
//Clear a list
|
|
|
|
technologies.clear()
|
|
|
|
|
|
|
|
//Creating an empty map
|
|
|
|
def devMap = [:]
|
|
|
|
|
|
|
|
//Add values
|
|
|
|
devMap = ['name':'Roberto', 'framework':'Grails', 'language':'Groovy']
|
|
|
|
devMap.put('lastName','Perez')
|
|
|
|
|
|
|
|
//Iterate over elements of a map
|
|
|
|
devMap.each { println "$it.key: $it.value" }
|
|
|
|
devMap.eachWithIndex { it, i -> println "$i: $it"}
|
|
|
|
|
|
|
|
//Evaluate if a map contains a key
|
|
|
|
assert devMap.containsKey('name')
|
|
|
|
|
|
|
|
//Evaluate if a map contains a value
|
|
|
|
assert devMap.containsValue('Roberto')
|
|
|
|
|
|
|
|
//Get the keys of a map
|
|
|
|
println devMap.keySet()
|
|
|
|
|
|
|
|
//Get the values of a map
|
|
|
|
println devMap.values()
|
|
|
|
|
|
|
|
/*
|
|
|
|
Groovy Beans
|
|
|
|
|
|
|
|
GroovyBeans are JavaBeans but using a much simpler syntax
|
|
|
|
|
|
|
|
When Groovy is compiled to bytecode, the following rules are used.
|
|
|
|
|
2013-08-19 20:14:02 +04:00
|
|
|
* If the name is declared with an access modifier (public, private or
|
|
|
|
protected) then a field is generated.
|
|
|
|
|
|
|
|
* A name declared with no access modifier generates a private field with
|
|
|
|
public getter and setter (i.e. a property).
|
|
|
|
|
|
|
|
* If a property is declared final the private field is created final and no
|
|
|
|
setter is generated.
|
|
|
|
|
2013-08-17 20:23:36 +04:00
|
|
|
* You can declare a property and also declare your own getter or setter.
|
2013-08-19 20:14:02 +04:00
|
|
|
|
|
|
|
* You can declare a property and a field of the same name, the property will
|
|
|
|
use that field then.
|
|
|
|
|
|
|
|
* If you want a private or protected property you have to provide your own
|
|
|
|
getter and setter which must be declared private or protected.
|
|
|
|
|
|
|
|
* If you access a property from within the class the property is defined in
|
|
|
|
at compile time with implicit or explicit this (for example this.foo, or
|
|
|
|
simply foo), Groovy will access the field directly instead of going though
|
|
|
|
the getter and setter.
|
|
|
|
|
|
|
|
* If you access a property that does not exist using the explicit or
|
|
|
|
implicit foo, then Groovy will access the property through the meta class,
|
|
|
|
which may fail at runtime.
|
2013-08-17 20:23:36 +04:00
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
class Foo {
|
|
|
|
// read only property
|
|
|
|
final String name = "Roberto"
|
|
|
|
|
|
|
|
// read only property with public getter and protected setter
|
|
|
|
String language
|
|
|
|
protected void setLanguage(String language) { this.language = language }
|
|
|
|
|
|
|
|
// dynamically typed property
|
|
|
|
def lastName
|
|
|
|
}
|
|
|
|
|
2019-08-30 17:58:06 +03:00
|
|
|
/*
|
|
|
|
Methods with optional parameters
|
|
|
|
*/
|
|
|
|
|
2020-04-17 16:52:38 +03:00
|
|
|
// A method can have default values for parameters
|
2019-08-30 17:58:06 +03:00
|
|
|
def say(msg = 'Hello', name = 'world') {
|
|
|
|
"$msg $name!"
|
|
|
|
}
|
|
|
|
|
|
|
|
// It can be called in 3 different ways
|
|
|
|
assert 'Hello world!' == say()
|
|
|
|
// Right most parameter with default value is eliminated first.
|
|
|
|
assert 'Hi world!' == say('Hi')
|
2023-04-10 19:52:49 +03:00
|
|
|
assert 'learn groovy!' == say('learn', 'groovy')
|
2019-08-30 17:58:06 +03:00
|
|
|
|
2013-08-17 20:23:36 +04:00
|
|
|
/*
|
|
|
|
Logical Branching and Looping
|
|
|
|
*/
|
|
|
|
|
|
|
|
//Groovy supports the usual if - else syntax
|
|
|
|
def x = 3
|
|
|
|
|
|
|
|
if(x==1) {
|
|
|
|
println "One"
|
|
|
|
} else if(x==2) {
|
|
|
|
println "Two"
|
|
|
|
} else {
|
|
|
|
println "X greater than Two"
|
|
|
|
}
|
|
|
|
|
|
|
|
//Groovy also supports the ternary operator:
|
|
|
|
def y = 10
|
|
|
|
def x = (y > 1) ? "worked" : "failed"
|
|
|
|
assert x == "worked"
|
|
|
|
|
2015-10-06 21:09:29 +03:00
|
|
|
//Groovy supports 'The Elvis Operator' too!
|
|
|
|
//Instead of using the ternary operator:
|
|
|
|
|
|
|
|
displayName = user.name ? user.name : 'Anonymous'
|
|
|
|
|
2015-10-06 22:49:21 +03:00
|
|
|
//We can write it:
|
2015-10-06 21:09:29 +03:00
|
|
|
displayName = user.name ?: 'Anonymous'
|
|
|
|
|
2013-08-17 20:23:36 +04:00
|
|
|
//For loop
|
|
|
|
//Iterate over a range
|
|
|
|
def x = 0
|
|
|
|
for (i in 0 .. 30) {
|
|
|
|
x += i
|
|
|
|
}
|
|
|
|
|
|
|
|
//Iterate over a list
|
|
|
|
x = 0
|
|
|
|
for( i in [5,3,2,1] ) {
|
|
|
|
x += i
|
|
|
|
}
|
|
|
|
|
|
|
|
//Iterate over an array
|
|
|
|
array = (0..20).toArray()
|
|
|
|
x = 0
|
|
|
|
for (i in array) {
|
|
|
|
x += i
|
|
|
|
}
|
|
|
|
|
|
|
|
//Iterate over a map
|
|
|
|
def map = ['name':'Roberto', 'framework':'Grails', 'language':'Groovy']
|
2017-10-04 17:15:38 +03:00
|
|
|
x = ""
|
2013-08-17 20:23:36 +04:00
|
|
|
for ( e in map ) {
|
|
|
|
x += e.value
|
2017-10-04 17:15:38 +03:00
|
|
|
x += " "
|
2013-08-17 20:23:36 +04:00
|
|
|
}
|
2017-10-04 17:15:38 +03:00
|
|
|
assert x.equals("Roberto Grails Groovy ")
|
2013-08-17 19:19:47 +04:00
|
|
|
|
2013-08-17 21:44:19 +04:00
|
|
|
/*
|
|
|
|
Operators
|
|
|
|
|
2013-08-19 20:14:02 +04:00
|
|
|
Operator Overloading for a list of the common operators that Groovy supports:
|
2015-06-16 17:42:55 +03:00
|
|
|
http://www.groovy-lang.org/operators.html#Operator-Overloading
|
2013-08-17 21:44:19 +04:00
|
|
|
|
|
|
|
Helpful groovy operators
|
|
|
|
*/
|
|
|
|
//Spread operator: invoke an action on all items of an aggregate object.
|
|
|
|
def technologies = ['Groovy','Grails','Gradle']
|
2013-08-19 20:14:02 +04:00
|
|
|
technologies*.toUpperCase() // = to technologies.collect { it?.toUpperCase() }
|
2013-08-17 21:44:19 +04:00
|
|
|
|
|
|
|
//Safe navigation operator: used to avoid a NullPointerException.
|
|
|
|
def user = User.get(1)
|
|
|
|
def username = user?.username
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
Closures
|
2013-08-19 20:14:02 +04:00
|
|
|
A Groovy Closure is like a "code block" or a method pointer. It is a piece of
|
|
|
|
code that is defined and then executed at a later point.
|
2013-08-17 21:44:19 +04:00
|
|
|
|
2015-06-16 17:42:55 +03:00
|
|
|
More info at: http://www.groovy-lang.org/closures.html
|
2013-08-17 21:44:19 +04:00
|
|
|
*/
|
|
|
|
//Example:
|
|
|
|
def clos = { println "Hello World!" }
|
|
|
|
|
|
|
|
println "Executing the Closure:"
|
|
|
|
clos()
|
|
|
|
|
|
|
|
//Passing parameters to a closure
|
|
|
|
def sum = { a, b -> println a+b }
|
|
|
|
sum(2,4)
|
|
|
|
|
|
|
|
//Closures may refer to variables not listed in their parameter list.
|
|
|
|
def x = 5
|
|
|
|
def multiplyBy = { num -> num * x }
|
|
|
|
println multiplyBy(10)
|
|
|
|
|
2013-08-19 20:14:02 +04:00
|
|
|
// If you have a Closure that takes a single argument, you may omit the
|
|
|
|
// parameter definition of the Closure
|
2013-08-17 21:44:19 +04:00
|
|
|
def clos = { print it }
|
|
|
|
clos( "hi" )
|
|
|
|
|
|
|
|
/*
|
2024-06-03 22:31:20 +03:00
|
|
|
Groovy can memoize closure results
|
2013-08-17 21:45:13 +04:00
|
|
|
*/
|
2013-08-17 21:44:19 +04:00
|
|
|
def cl = {a, b ->
|
|
|
|
sleep(3000) // simulate some time consuming processing
|
|
|
|
a + b
|
|
|
|
}
|
|
|
|
|
|
|
|
mem = cl.memoize()
|
|
|
|
|
|
|
|
def callClosure(a, b) {
|
|
|
|
def start = System.currentTimeMillis()
|
|
|
|
mem(a, b)
|
|
|
|
println "Inputs(a = $a, b = $b) - took ${System.currentTimeMillis() - start} msecs."
|
|
|
|
}
|
|
|
|
|
|
|
|
callClosure(1, 2)
|
|
|
|
callClosure(1, 2)
|
|
|
|
callClosure(2, 3)
|
|
|
|
callClosure(2, 3)
|
|
|
|
callClosure(3, 4)
|
|
|
|
callClosure(3, 4)
|
|
|
|
callClosure(1, 2)
|
|
|
|
callClosure(2, 3)
|
|
|
|
callClosure(3, 4)
|
|
|
|
|
|
|
|
/*
|
|
|
|
Expando
|
|
|
|
|
2013-08-19 20:14:02 +04:00
|
|
|
The Expando class is a dynamic bean so we can add properties and we can add
|
|
|
|
closures as methods to an instance of this class
|
2013-08-17 21:44:19 +04:00
|
|
|
|
2013-08-19 20:14:02 +04:00
|
|
|
http://mrhaki.blogspot.mx/2009/10/groovy-goodness-expando-as-dynamic-bean.html
|
2013-08-17 21:44:19 +04:00
|
|
|
*/
|
|
|
|
def user = new Expando(name:"Roberto")
|
|
|
|
assert 'Roberto' == user.name
|
|
|
|
|
|
|
|
user.lastName = 'Pérez'
|
|
|
|
assert 'Pérez' == user.lastName
|
|
|
|
|
|
|
|
user.showInfo = { out ->
|
|
|
|
out << "Name: $name"
|
|
|
|
out << ", Last name: $lastName"
|
|
|
|
}
|
|
|
|
|
|
|
|
def sw = new StringWriter()
|
|
|
|
println user.showInfo(sw)
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
Metaprogramming (MOP)
|
|
|
|
*/
|
|
|
|
|
|
|
|
//Using ExpandoMetaClass to add behaviour
|
|
|
|
String.metaClass.testAdd = {
|
|
|
|
println "we added this"
|
|
|
|
}
|
|
|
|
|
|
|
|
String x = "test"
|
|
|
|
x?.testAdd()
|
|
|
|
|
|
|
|
//Intercepting method calls
|
|
|
|
class Test implements GroovyInterceptable {
|
|
|
|
def sum(Integer x, Integer y) { x + y }
|
|
|
|
|
|
|
|
def invokeMethod(String name, args) {
|
|
|
|
System.out.println "Invoke method $name with args: $args"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
def test = new Test()
|
|
|
|
test?.sum(2,3)
|
|
|
|
test?.multiply(2,3)
|
|
|
|
|
|
|
|
//Groovy supports propertyMissing for dealing with property resolution attempts.
|
|
|
|
class Foo {
|
|
|
|
def propertyMissing(String name) { name }
|
|
|
|
}
|
|
|
|
def f = new Foo()
|
|
|
|
|
|
|
|
assertEquals "boo", f.boo
|
|
|
|
|
|
|
|
/*
|
|
|
|
TypeChecked and CompileStatic
|
2013-08-19 20:14:02 +04:00
|
|
|
Groovy, by nature, is and will always be a dynamic language but it supports
|
|
|
|
typechecked and compilestatic
|
2013-08-17 21:44:19 +04:00
|
|
|
|
|
|
|
More info: http://www.infoq.com/articles/new-groovy-20
|
|
|
|
*/
|
|
|
|
//TypeChecked
|
|
|
|
import groovy.transform.TypeChecked
|
|
|
|
|
|
|
|
void testMethod() {}
|
|
|
|
|
|
|
|
@TypeChecked
|
|
|
|
void test() {
|
|
|
|
testMeethod()
|
|
|
|
|
|
|
|
def name = "Roberto"
|
|
|
|
|
|
|
|
println naameee
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
//Another example:
|
|
|
|
import groovy.transform.TypeChecked
|
|
|
|
|
|
|
|
@TypeChecked
|
|
|
|
Integer test() {
|
|
|
|
Integer num = "1"
|
|
|
|
|
|
|
|
Integer[] numbers = [1,2,3,4]
|
|
|
|
|
|
|
|
Date date = numbers[1]
|
|
|
|
|
|
|
|
return "Test"
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
//CompileStatic example:
|
|
|
|
import groovy.transform.CompileStatic
|
|
|
|
|
|
|
|
@CompileStatic
|
|
|
|
int sum(int x, int y) {
|
|
|
|
x + y
|
|
|
|
}
|
|
|
|
|
|
|
|
assert sum(2,5) == 7
|
2013-08-17 19:19:47 +04:00
|
|
|
```
|
|
|
|
|
|
|
|
## Further resources
|
|
|
|
|
2015-06-07 20:06:05 +03:00
|
|
|
[Groovy documentation](http://www.groovy-lang.org/documentation.html)
|
2013-08-17 19:19:47 +04:00
|
|
|
|
|
|
|
[Groovy web console](http://groovyconsole.appspot.com/)
|
|
|
|
|
2015-06-16 17:42:55 +03:00
|
|
|
Join a [Groovy user group](http://www.groovy-lang.org/usergroups.html)
|
2013-08-17 19:19:47 +04:00
|
|
|
|
|
|
|
## Books
|
|
|
|
|
2024-06-03 22:31:20 +03:00
|
|
|
* [Groovy Goodness](https://leanpub.com/groovy-goodness-notebook)
|
|
|
|
* [Groovy in Action](http://manning.com/koenig2/)
|
|
|
|
* [Programming Groovy 2: Dynamic Productivity for the Java Developer](http://shop.oreilly.com/product/9781937785307.do)
|