Groovy!
There and Back Again
Hello!
I am Petr Giecek
Software Engineer @ Hewlett Packard Enterprise
1.
What is Groovy?
Groovy Programming Language
Dynamic JVM language
Static typing and static compilation capabilities
Familiar and easy to learn syntax for Java
developers
Concise, readable and expressive
Smooth Java integration
Interoperates with Java and any third-party Java libraries
TIOBE Index for May 2016
Groovy distribution
Download from Bintray
https://bintray.com/groovy/
SDKMAN! (The Software Development Kit Manager)
OSX, Linux, Cygwin
Posh-GVM (POwerSHell Groovy enVironment Manager)
Windows
The Groovy Development Kit
A number of helper methods added to the JDK
Working with IO
Working with collections
Working with threads
Handy utilities
...
Tools
groovy
Groovy interpreter
groovyc
Groovy compiler
groovysh
Groovy shell
groovyConsole
Groovy Swing console
Dynamic language
def var = 4
assert var.class == Integer
var = 'Hello'
assert var.class == String
var = true
assert var.class == Boolean
Dynamically-typed language
String greeting = "Hello";
// Compile-time error
greeting / 5
Java Groovy
def greeting = 'Hello'
// Runtime error
greeting / 5
Optionally typed language
// Runtime error: GroovyCastException
int greeting = '123'
Static type checking
@groovy.transform.TypeChecked
class Calculator {
static int sum(int x, int y) {
return "Result is ${x + y}" // Compile-time error
}
}
assert Calculator.sum(1, 10) == 11
Type inference
def message = 'Groovy is awesome!'
assert message.toUpperCase() == 'GROOVY IS AWESOME!'
assert message.upper() == 'GROOVY IS AWESOME!' // Compile-time error
Flow typing
def value = 'Groovy is awesome'
value = value.toUpperCase()
assert value == 'GROOVY IS AWESOME'
value = 9d
value = Math.sqrt(value)
assert value == 3.0
value = value.toLowerCase(value) // Compile-time error
Static compilation
class User {
String name
int age
}
@groovy.transform.CompileStatic
def test() {
def user = new User(name: 'John Smith', age: 30)
int age = user.age // getAge() possible runtime error: GroovyCastException
}
User.metaClass.getAge = { -> "30" } // runtime modification
test()
Everything clear?
2.
Hello Groovy!
Syntax
The majority of Java syntax is valid Groovy
Primitive types
Packages and imports
Control flow statements
◇ More flexible switch
◇ For-in loop
Exception handling
◇ No distinction between checked and unchecked exceptions
Classes and object instantiation
You are Groovy programmers!
Extra keywords
� as
� def
� in
� trait
Default imports
java.io.*
java.lang.*
java.math.BigDecimal
java.math.BigInteger
Everything is an object
char c = 'C'
assert c.class == Character
int num1 = 5
assert num1.class == Integer
def num2 = 9223372036854775808
assert num2.class == BigInteger
def num3 = 10.5
assert num3.class == BigDecimal
def bool = true
assert bool.class == Boolean
Division
assert 10 / 4 == 2.5
assert 10.intdiv(4) == 2
String literals
assert 'Hello World'.class == String
assert "Hello World".class == String
assert "Hello ${name}".class instanceof GString
def c1 = "C"
assert c1.class == String
char c2 = c1
assert c2.class == Character
Default scope
public class User {}
class User {}
Java Groovy
class User {}
@PackageScope class User {}
Equality/identity check
String s1 = "Hello"
String s2 = new String("Hello")
assert s1.equals(s2) // equality check
assert s1 != s2 // identity check
Java Groovy
def s1 = 'Hello'
def s2 = new String('Hello')
assert s1 == s2 // equality check
assert !s1.is(s2) // identity check
Lambdas
Runnable run =
() -> System.out.println("Run");
Java Groovy
Runnable run = { println 'Run' }
Runtime dispatch (multimethods)
String whoIsAwesome(String arg) {
return "Groovy is awesome!";
}
String whoIsAwesome(Object arg) {
return "Java is awesome!";
}
Object o = "Object";
String result = whoIsAwesome(o);
assertEquals("Java is awesome", result);
Java
assertEquals("Groovy is awesome",
result);
Groovy
Other differences with Java
Interfaces do not support default implementation (Java 8)
Automatic resource management blocks not supported (Java 7)
No difference between checked and uncheck exceptions
Assertion in Groovy is always enabled
power asserts
3.
Goodbye Java
“
Boilerplate refers to code that have to
be included in many places with little or
no alteration.
Hello...
public class Main {
public static void main(String... args) {
System.out.println("Goodbye Java!")
}
}
Java Groovy
println 'Hello Groovy!'
Optionality
Optional parentheses
Optional semicolons
Optional return keyword
Optional public keyword
The Groovy Truth
assert [1, 2, 3]
assert ![] // Empty collections and arrays
assert [1, 2, 3].iterator()
assert ![].iterator() // Iterators and Enumerations without further elements
assert [name: 'John']
assert ![:] // Empty maps
assert 'Hello Groovy'
assert !'' // Empty Strings, GStrings and CharSequences
assert 100
assert !0 // Zero number
assert new Object()
assert !null // Null object references
Example
if (users == null || users.isEmpty()) {
throw new IllegalArgumentException("No users found")
}
Java
if (!users) {
throw new IllegalArgumentException('No users found')
}
Groovy
Lists
def numbers = [1, 2, 3, 4]
assert numbers.class == ArrayList
def everything = [1, 2.5, true, 'Hello']
assert everything.class == ArrayList
Lists (cont’d)
def numbers1 = [1, 2, 3, 4] as LinkedList
assert numbers1.class == LinkedList
LinkedList numbers2 = [1, 2, 3, 4]
assert numbers2.class == LinkedList
Lists (cont’d)
def letters = ['a', 'b', 'c', 'd']
assert letters[0] == 'a'
assert letters[1] == 'b'
assert letters[-1] == 'd'
assert letters[-2] == 'c'
letters[2] = 'C'
assert letters[2] == 'C'
letters << 'e'
assert letters[-1] == 'e'
assert letters[1, 3] == ['b', 'd']
assert letters[2..4] == ['C', 'd', 'e']
Arrays
def languages = ['Java', 'Groovy', 'Scala'] as String[]
assert languages instanceof String[]
int[] numbers = [1, 2, 3]
assert numbers instanceof int[]
Maps
def colors = [red: '#FF0000', green: '#00FF00', blue: '#0000FF']
assert colors['red'] == '#FF0000'
assert colors.green == '#00FF00'
colors['pink'] = '#FF00FF'
colors.yellow = '#FFFF00'
assert colors.pink == '#FF00FF'
assert colors['yellow'] == '#FFFF00'
assert colors.getClass() == LinkedHashMap
Ranges
assert (0..5) == [0, 1, 2, 3, 4, 5]
assert ('a'..<'f') == ['a', 'b', 'c', 'd', 'e']
assert (2..-2) == [2, 1, 0, -1, -2]
assert (0..10).step(2) == [0, 2, 4, 6, 8, 10]
assert (0..5) instanceof Range
assert (0..5) instanceof List
Range in loops
for (i = 0; i < 10; i++) {
println i
}
for (i in 0..<10) {
println i
}
Elvis operator (?:)
def displayName = user.name ? user.name : 'Unknown'
def displayName = user.name ?: 'Unknown'
Safe navigation operator (?.)
if (headers == null || headers.getContentType() == null || headers.getContentType().getCharSet() == null) {
return DEFAULT_CHARSET;
}
return headers.getContentType().getCharSet();
Java
def charset = headers?.contentType?.charset
if (!charset) {
return DEFAULT_CHARSET;
}
charset
Groovy
Spaceship operator (<=>)
assert (1 <=> 1) == 0
assert (1 <=> 100) == -1
assert (100 <=> 1) == 1
assert ('a' <=> 'z') == -1
assert ('Groovy is awesome' <=> 'Java is not so bad') == -1
Spread operator (*.)
class User {
String name
int age
}
def users = [
new User(name: 'John', age: 33),
new User(name: 'George', age: 35),
null]
assert users*.name == ['John', 'George', null]
Spreading method arguments
int add(int a, int b, int c, int d) {
a + b + c + d
}
def args = [1, 2, 7, 10]
assert add(*args) == 20
args = [1, 2, 7]
assert add(*args, 10) == 20
Operator overloading
+ a.plus(b)
- a.minus(b)
* a.multiply(b)
/ a.div(b)
% a.mod(b)
** a.power(b)
| a.or(b)
& a.and(b)
^ a.xor(b)
as a.asType(b)
a() a.call()
a[b] a.getAt(b)
a[b] = c a.putAt(b, c)
in a.isCase(b)
<< a.leftShift(b)
>> a.rightShift(b)
++ a.next()
-- a.previous(b)
Coercion
BigDecimal pi1 = (BigDecimal) '3.14159265359' // GroovyCastException
BigDecimal pi2 = '3.14159265359' as BigDecimal
List to type coercion
class User {
String name
int age
User(String name, int age) {
this.name = name
this.age = age
}
}
def user1 = ['John', 30] as User
User user2 = ['John', 30]
Map to type coercion
def map
map = [
i: 10,
hasNext: { map.i > 0 },
next: { map.i-- },
]
def iter = map as Iterator
assert iter.collect { it } == [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
Runtime interface implementation
interface Greeter {
def sayHello(String name)
}
class DefaultGreeter {
def sayHello(String name) { println "Hello $name" }
}
def greeter = new DefaultGreeter() as Greeter
assert greeter instanceof Greeter
Constructors (positional arguments)
// Constructor invocation with new keyword
def user1 = new User('John', 30)
// Using coercion with as keyword
def user2 = ['John', 30] as User
// Using coercion in assignment
User user3 = ['John', 30]
Constructors (named arguments)
def user1 = new User()
def user2 = new User(name: 'John')
def user3 = new User(age: 30)
def user4 = new User(name: 'John', age: 30)
Methods
def sayHello() { 'Hello Groovy!' }
String sayHello2(def name) { 'Hello $name!' }
def sayHello3(name) { "Hello $name!" }
static String sayHello4(String name) { "Hello $name!" }
Named arguments
def createUser(Map args) { new User(args.name, args['age']) }
def user = createUser(name: 'John', age: 30)
assert user.name == 'John'
assert user.age == 30
Default arguments
def createUser(String name, int age = 30) { new User(name, age)
}
def user = createUser('John')
assert user.name == 'John'
assert user.age == 30
Properties
class User {
final String name = 'Jesus'
int age
}
class User {
private final String name = 'Jesus'
private int age
String getName() { return name }
void setAge(int age) { this.age = age }
int getAge() { return age }
}
Property access
def user = new User()
user.age = 33
user.name = 'Moses' // ReadOnlyPropertyException
assert user.name == 'Jesus'
assert user.age == 33
Direct field access
class User {
public String name
String getName() {
"User's name is $name"
}
}
def user = new User(name: 'John')
assert user.name == "User's name is John"
class User {
public String name
String getName() {
"User's name is $name"
}
}
def user = new User(name: 'John')
assert user.@name == 'John'
4.
Closures
Closures syntax
{ [parameters -> ] statements }
Closure examples
{ 1 + 5 }
{ -> 1 + 5 }
{ println it }
{ num -> println num }
{ int x, int y = 10 -> "Sum of $x and $y is ${x + y}"}
def closure = { Writer w ->
w.writeLine 'Groovy creates a new BufferedWriter for the file and passes it to the closure.'
w.writeLine 'It also ensures the stream is flushed and closed after the closure returns.'
w.writeLine 'Even if an exception is thrown in the closure.'
}
assert closure instanceof Closure
def file = new File('sample.txt')
file.withWriter(closure)
Closure is an object
Calling a closure
def sayHello1 = { 'Hello Groovy!' }
assert sayHello1() == 'Hello Groovy!'
assert sayHello1.call() == 'Hello Groovy!'
def sayHello2 = { "Hello $it!" }
assert sayHello2('Groovy') == 'Hello Groovy!'
assert sayHello2.call('Groovy') == 'Hello Groovy!'
def sayHello3 = { name -> "Hello $name!" }
assert sayHello3('Groovy') == 'Hello Groovy!'
assert sayHello3.call('Groovy') == 'Hello Groovy!'
Method pointer
def sayHello(String name) { "Hello $name" }
def sayHello(int nTimes) { 'Hello' * nTimes }
def pointer = this.&sayHello
assert pointer instanceof Closure
assert pointer('John') == 'Hello John'
assert pointer(3) == 'HelloHelloHello'
Closure coercion
interface Predicate<T> {
boolean accept(T obj)
}
def filter2 = { it.startsWith 'J' } as Predicate
assert filter2.accept('Java')
Predicate filter1 = { it.startsWith 'G' }
assert filter1.accept('Groovy')
Methods accepting a SAM type
static <T> List<T> filter(List<T> source, Predicate<T> predicate) {
source.findAll { predicate.accept(it) }
}
assert filter(['Java', 'Groovy'], { it.contains 'G' }) == ['Groovy']
assert filter(['Java', 'Groovy']) { it.contains 'G' } == ['Groovy']
� this
The enclosing class of a closure definition
� owner
The enclosing object of a closure definition (a class or a closure)
� delegate
An object where methods calls or properties are resolved if no
receiver
of the message is defined
Delegation
Example
class User {
def name
}
def user = new User(name: 'John')
def getUserName = { "The name is $name" }
assert getUserName() == 'The name is John' // MissingPropertyException
getUserName.delegate = user
assert getUserName() == 'The name is John'
Example (Gradle)
apply plugin: 'java'
apply plugin: 'eclipse'
sourceCompatibility = 1.5
version = '1.0'
repositories {
mavenCentral()
}
dependencies {
compile group: 'commons-collections', name: 'commons-collections', version: '3.2.2'
testCompile group: 'junit', name: 'junit', version: '4.+'
}
Closures
First class citizens of type groovy.lang.Closure
Can access any variables of the surrounding context
Support delegation concept
Method reference via GroovyClass.&groovyMethod
Lambda expressions
Instances of SAM type they represent
Can access only effectively final variables of the surrounding context
Method reference via JavaClass::javaMethod
Closures vs Java 8 lambda expressions
5.
Metaprogramming
“
Metaprogramming is the writing of
computer programs with the ability to
treat programs as their data.
Flavors
Runtime
Compile-time
Runtime metaprogramming
POJO
Regular Java object
Class written in Java or other JVM language
POGO
Class written in Groovy
Extends java.lang.Object and implements groovy.lang.GroovyObject
GroovyInterceptable
All methods intercepted through invokeMethod
POGO method handling
GroovyObject interface
public interface GroovyObject {
Object invokeMethod(String name, Object args);
Object getProperty(String propertyName);
void setProperty(String propertyName, Object newValue);
MetaClass getMetaClass();
void setMetaClass(MetaClass metaClass);
}
invokeMethod
class InvokeMethodExample implements GroovyInterceptable {
def existingMethod() { 'existingMethod()' }
def invokeMethod(String name, Object args) {
def m = metaClass.getMetaMethod(name, *args)
if (!m) return "Handle unknown method: $name($args)"
return "Intercepted: ${m.invoke(this, *args)}"
}
}
def obj = new InvokeMethodExample()
assert obj.existingMethod() == 'Intercepted: existingMethod()'
assert obj.newMethod(true) == 'Handle unknown method: newMethod([true])'
methodMissing
class User {
def methodMissing(String name, def args) {
if (name.startsWith("findBy")) {
def field = name[6].toLowerCase() + name[7..-1]
return "Looking for a user by $field=${args.first()}"
} else
throw new MissingMethodException(name, this.class, args)
}
}
def user = new User()
assert user.findByFirstName('John') == 'Looking for a user by firstName=John'
assert user.findByAge(30) == 'Looking for a user by age=30'
Example
def writer = new StringWriter()
def xml = new MarkupBuilder(writer)
xml.movies() {
movie(year: 1977) {
name('Star Wars: Episode IV')
director('George Lucas')
}
movie(year: 1989) {
name('Indiana Jines and the Last Crusade')
director('Steven Spielberg')
}
}
Example (cont’d)
<movies>
<movie year='1977'>
<name>Star Wars: Episode IV</name>
<director>George Lucas</director>
</movie>
<movie year='1989'>
<name>Indiana Jines and the Last Crusade</name>
<director>Steven Spielberg</director>
</movie>
</movies>
MetaClass
Defines the behaviour of any Groovy or Java class
Client API defined via MetaObjectProtocol
Contract with Groovy runtime system
Cached in MetaClassRegistry
Available via metaClass property
ExpandoMetaClass
Allows for dynamically adding/changing
Methods
Constructors
Properties
Adding/changing methods
class User {
String name
}
User.metaClass.nameInUpperCase = { -> name.toUpperCase() }
User.metaClass.nameInLowerCase << { -> name.toLowerCase() } // Has to be a new method
def user = new User(name: 'John Smith')
assert user.nameInUpperCase() == 'JOHN SMITH'
assert user.nameInLowerCase() == 'john smith'
Adding/changing constructors
class User {
String name
}
User.metaClass.constructor << { String name -> new User(name: name) }
def user = new User('John Smith')
assert user.name == 'John Smith'
Adding/changing properties
class User {
String name
}
User.metaClass.age = 30
def user = new User(name: 'John Smith')
assert user.age == 30
user.age = 35
assert user.age == 35
Adding/changing static methods
class User {
String name
}
User.metaClass.static.newInstance << { name -> new User(name: name) }
def user = User.newInstance('John Smith')
assert user.name == 'John Smith'
Compile-time metaprogramming
Allows code generation at compile-time
Alters Abstract Syntax Tree (AST) of a program
The changes are visible in bytecode
Callable from other JVM languages
Usually better performance
AST transformations
Global AST transformation
Applied as soons as they are found on compile classpath
No out-of-the-box transformations available
Local AST transformation
Applied by annotating the source code with markers
Supports parameters
Available AST transformations
Reducing boilerplate (code generation)
Implementing design patterns
Logging
Declarative concurrency
Cloning
Testing
...
Examples
� @groovy.transform.ToString
� @groovy.transform.EqualsAndHashCode
� @groovy.transform.TupleConstructor
� @groovy.transform.Canonical
@groovy.transform.InheritConstructors
Available AST transformations (cont’d)
� @groovy.lang.Lazy
� @groovy.transform.Sortable
� @groovy.transform.builder.Builder
� @groovy.lang.Delegate
� @groovy.transform.Immutable
6.
From prototype...
“
It is about how fast you can write the code.
“
If you can, go with a language and tools you
already know.
Strengths
Familiar, expressive and succinct syntax
Smooth Java integration
Vibrant and rich ecosystem
Rich and easy-to-use API
Embedded dependency manager
7.
… to production
“
It is about how fast you can understand
the code.
What does this code do?
def process(def arg1, def arg2, def arg3) {
[arg1, arg2, arg3].inject('') { x, y ->
if (y <= 0) return "${x}00"
if (y > 255) return "${x}ff"
def s = ''
for (int n = y; n > 0; n = n.intdiv(16)) {
int r = n % 16
s = r < 10 ? r + s : "${(char) (((int) 'A') - 10 + r)}$s"
}
s.length() == 1 ? "${x}0$s" : "$x$s"
}
}
And now?
String rgbToHex(int r, int g, int b) {
[r, g, b].inject('') { acc, val ->
if (val <= 0) return "${acc}00"
if (val > 255) return "${acc}ff"
def hex = ''
for (int i = val; i > 0; i = i.intdiv(16)) {
int j = i % 16
hex = j < 10 ? j + hex : "${(char) (((int) 'A') - 10 + j)}$hex"
}
hex.length() == 1 ? "${acc}0$hex" : "$acc$hex"
}
}
Best practices
Use explicit types for public APIs
Interfaces
Document client contract
Use descriptive names (for everything)
Avoid using the same variable name for different things
Use optional elements explicitly if it helps code readability
parentheses, return keyword
Best practices (cont’d)
Restrict access to members that are not part of API
private, protected, @PackageScope
Use @TypeChecked or @CompileStatic capabilities if possible
Keep your code segments small and isolated
Especially if making use of duck typing
Cover code with as many test suites as reasonable
Refactoring
“
Groovy is not a replacement for Java, it
is addition to Java.
Java?
This is Groovy!
References
Groovy official site
http://www.groovy-lang.org
Groovy Goodness
http://mrhaki.blogspot.cz
Thanks!
Any questions?
You can find me at petr.giecek.82@gmail.com
https://www.linkedin.com/in/petrgiecek
https://github.com/pgiecek
Credits
Special thanks to all the people who made and
released these awesome resources for free:
Presentation template by SlidesCarnival
Photographs by Unsplash

Groovy!

  • 1.
  • 2.
    Hello! I am PetrGiecek Software Engineer @ Hewlett Packard Enterprise
  • 3.
  • 4.
    Groovy Programming Language DynamicJVM language Static typing and static compilation capabilities Familiar and easy to learn syntax for Java developers Concise, readable and expressive Smooth Java integration Interoperates with Java and any third-party Java libraries
  • 5.
  • 6.
    Groovy distribution Download fromBintray https://bintray.com/groovy/ SDKMAN! (The Software Development Kit Manager) OSX, Linux, Cygwin Posh-GVM (POwerSHell Groovy enVironment Manager) Windows
  • 7.
    The Groovy DevelopmentKit A number of helper methods added to the JDK Working with IO Working with collections Working with threads Handy utilities ...
  • 8.
  • 9.
    Dynamic language def var= 4 assert var.class == Integer var = 'Hello' assert var.class == String var = true assert var.class == Boolean
  • 10.
    Dynamically-typed language String greeting= "Hello"; // Compile-time error greeting / 5 Java Groovy def greeting = 'Hello' // Runtime error greeting / 5
  • 11.
    Optionally typed language //Runtime error: GroovyCastException int greeting = '123'
  • 13.
    Static type checking @groovy.transform.TypeChecked classCalculator { static int sum(int x, int y) { return "Result is ${x + y}" // Compile-time error } } assert Calculator.sum(1, 10) == 11
  • 14.
    Type inference def message= 'Groovy is awesome!' assert message.toUpperCase() == 'GROOVY IS AWESOME!' assert message.upper() == 'GROOVY IS AWESOME!' // Compile-time error
  • 15.
    Flow typing def value= 'Groovy is awesome' value = value.toUpperCase() assert value == 'GROOVY IS AWESOME' value = 9d value = Math.sqrt(value) assert value == 3.0 value = value.toLowerCase(value) // Compile-time error
  • 16.
    Static compilation class User{ String name int age } @groovy.transform.CompileStatic def test() { def user = new User(name: 'John Smith', age: 30) int age = user.age // getAge() possible runtime error: GroovyCastException } User.metaClass.getAge = { -> "30" } // runtime modification test()
  • 17.
  • 18.
  • 19.
    Syntax The majority ofJava syntax is valid Groovy Primitive types Packages and imports Control flow statements ◇ More flexible switch ◇ For-in loop Exception handling ◇ No distinction between checked and unchecked exceptions Classes and object instantiation
  • 20.
    You are Groovyprogrammers!
  • 21.
    Extra keywords � as �def � in � trait
  • 22.
  • 23.
    Everything is anobject char c = 'C' assert c.class == Character int num1 = 5 assert num1.class == Integer def num2 = 9223372036854775808 assert num2.class == BigInteger def num3 = 10.5 assert num3.class == BigDecimal def bool = true assert bool.class == Boolean
  • 24.
    Division assert 10 /4 == 2.5 assert 10.intdiv(4) == 2
  • 25.
    String literals assert 'HelloWorld'.class == String assert "Hello World".class == String assert "Hello ${name}".class instanceof GString def c1 = "C" assert c1.class == String char c2 = c1 assert c2.class == Character
  • 26.
    Default scope public classUser {} class User {} Java Groovy class User {} @PackageScope class User {}
  • 27.
    Equality/identity check String s1= "Hello" String s2 = new String("Hello") assert s1.equals(s2) // equality check assert s1 != s2 // identity check Java Groovy def s1 = 'Hello' def s2 = new String('Hello') assert s1 == s2 // equality check assert !s1.is(s2) // identity check
  • 28.
    Lambdas Runnable run = ()-> System.out.println("Run"); Java Groovy Runnable run = { println 'Run' }
  • 29.
    Runtime dispatch (multimethods) StringwhoIsAwesome(String arg) { return "Groovy is awesome!"; } String whoIsAwesome(Object arg) { return "Java is awesome!"; } Object o = "Object"; String result = whoIsAwesome(o); assertEquals("Java is awesome", result); Java assertEquals("Groovy is awesome", result); Groovy
  • 30.
    Other differences withJava Interfaces do not support default implementation (Java 8) Automatic resource management blocks not supported (Java 7) No difference between checked and uncheck exceptions Assertion in Groovy is always enabled power asserts
  • 32.
  • 33.
    “ Boilerplate refers tocode that have to be included in many places with little or no alteration.
  • 34.
    Hello... public class Main{ public static void main(String... args) { System.out.println("Goodbye Java!") } } Java Groovy println 'Hello Groovy!'
  • 35.
  • 36.
    The Groovy Truth assert[1, 2, 3] assert ![] // Empty collections and arrays assert [1, 2, 3].iterator() assert ![].iterator() // Iterators and Enumerations without further elements assert [name: 'John'] assert ![:] // Empty maps assert 'Hello Groovy' assert !'' // Empty Strings, GStrings and CharSequences assert 100 assert !0 // Zero number assert new Object() assert !null // Null object references
  • 37.
    Example if (users ==null || users.isEmpty()) { throw new IllegalArgumentException("No users found") } Java if (!users) { throw new IllegalArgumentException('No users found') } Groovy
  • 38.
    Lists def numbers =[1, 2, 3, 4] assert numbers.class == ArrayList def everything = [1, 2.5, true, 'Hello'] assert everything.class == ArrayList
  • 39.
    Lists (cont’d) def numbers1= [1, 2, 3, 4] as LinkedList assert numbers1.class == LinkedList LinkedList numbers2 = [1, 2, 3, 4] assert numbers2.class == LinkedList
  • 40.
    Lists (cont’d) def letters= ['a', 'b', 'c', 'd'] assert letters[0] == 'a' assert letters[1] == 'b' assert letters[-1] == 'd' assert letters[-2] == 'c' letters[2] = 'C' assert letters[2] == 'C' letters << 'e' assert letters[-1] == 'e' assert letters[1, 3] == ['b', 'd'] assert letters[2..4] == ['C', 'd', 'e']
  • 41.
    Arrays def languages =['Java', 'Groovy', 'Scala'] as String[] assert languages instanceof String[] int[] numbers = [1, 2, 3] assert numbers instanceof int[]
  • 42.
    Maps def colors =[red: '#FF0000', green: '#00FF00', blue: '#0000FF'] assert colors['red'] == '#FF0000' assert colors.green == '#00FF00' colors['pink'] = '#FF00FF' colors.yellow = '#FFFF00' assert colors.pink == '#FF00FF' assert colors['yellow'] == '#FFFF00' assert colors.getClass() == LinkedHashMap
  • 43.
    Ranges assert (0..5) ==[0, 1, 2, 3, 4, 5] assert ('a'..<'f') == ['a', 'b', 'c', 'd', 'e'] assert (2..-2) == [2, 1, 0, -1, -2] assert (0..10).step(2) == [0, 2, 4, 6, 8, 10] assert (0..5) instanceof Range assert (0..5) instanceof List
  • 44.
    Range in loops for(i = 0; i < 10; i++) { println i } for (i in 0..<10) { println i }
  • 45.
    Elvis operator (?:) defdisplayName = user.name ? user.name : 'Unknown' def displayName = user.name ?: 'Unknown'
  • 46.
    Safe navigation operator(?.) if (headers == null || headers.getContentType() == null || headers.getContentType().getCharSet() == null) { return DEFAULT_CHARSET; } return headers.getContentType().getCharSet(); Java def charset = headers?.contentType?.charset if (!charset) { return DEFAULT_CHARSET; } charset Groovy
  • 48.
    Spaceship operator (<=>) assert(1 <=> 1) == 0 assert (1 <=> 100) == -1 assert (100 <=> 1) == 1 assert ('a' <=> 'z') == -1 assert ('Groovy is awesome' <=> 'Java is not so bad') == -1
  • 49.
    Spread operator (*.) classUser { String name int age } def users = [ new User(name: 'John', age: 33), new User(name: 'George', age: 35), null] assert users*.name == ['John', 'George', null]
  • 50.
    Spreading method arguments intadd(int a, int b, int c, int d) { a + b + c + d } def args = [1, 2, 7, 10] assert add(*args) == 20 args = [1, 2, 7] assert add(*args, 10) == 20
  • 51.
    Operator overloading + a.plus(b) -a.minus(b) * a.multiply(b) / a.div(b) % a.mod(b) ** a.power(b) | a.or(b) & a.and(b) ^ a.xor(b) as a.asType(b) a() a.call() a[b] a.getAt(b) a[b] = c a.putAt(b, c) in a.isCase(b) << a.leftShift(b) >> a.rightShift(b) ++ a.next() -- a.previous(b)
  • 52.
    Coercion BigDecimal pi1 =(BigDecimal) '3.14159265359' // GroovyCastException BigDecimal pi2 = '3.14159265359' as BigDecimal
  • 53.
    List to typecoercion class User { String name int age User(String name, int age) { this.name = name this.age = age } } def user1 = ['John', 30] as User User user2 = ['John', 30]
  • 54.
    Map to typecoercion def map map = [ i: 10, hasNext: { map.i > 0 }, next: { map.i-- }, ] def iter = map as Iterator assert iter.collect { it } == [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
  • 55.
    Runtime interface implementation interfaceGreeter { def sayHello(String name) } class DefaultGreeter { def sayHello(String name) { println "Hello $name" } } def greeter = new DefaultGreeter() as Greeter assert greeter instanceof Greeter
  • 56.
    Constructors (positional arguments) //Constructor invocation with new keyword def user1 = new User('John', 30) // Using coercion with as keyword def user2 = ['John', 30] as User // Using coercion in assignment User user3 = ['John', 30]
  • 57.
    Constructors (named arguments) defuser1 = new User() def user2 = new User(name: 'John') def user3 = new User(age: 30) def user4 = new User(name: 'John', age: 30)
  • 58.
    Methods def sayHello() {'Hello Groovy!' } String sayHello2(def name) { 'Hello $name!' } def sayHello3(name) { "Hello $name!" } static String sayHello4(String name) { "Hello $name!" }
  • 59.
    Named arguments def createUser(Mapargs) { new User(args.name, args['age']) } def user = createUser(name: 'John', age: 30) assert user.name == 'John' assert user.age == 30
  • 60.
    Default arguments def createUser(Stringname, int age = 30) { new User(name, age) } def user = createUser('John') assert user.name == 'John' assert user.age == 30
  • 61.
    Properties class User { finalString name = 'Jesus' int age } class User { private final String name = 'Jesus' private int age String getName() { return name } void setAge(int age) { this.age = age } int getAge() { return age } }
  • 62.
    Property access def user= new User() user.age = 33 user.name = 'Moses' // ReadOnlyPropertyException assert user.name == 'Jesus' assert user.age == 33
  • 63.
    Direct field access classUser { public String name String getName() { "User's name is $name" } } def user = new User(name: 'John') assert user.name == "User's name is John" class User { public String name String getName() { "User's name is $name" } } def user = new User(name: 'John') assert user.@name == 'John'
  • 64.
  • 65.
  • 66.
    Closure examples { 1+ 5 } { -> 1 + 5 } { println it } { num -> println num } { int x, int y = 10 -> "Sum of $x and $y is ${x + y}"}
  • 67.
    def closure ={ Writer w -> w.writeLine 'Groovy creates a new BufferedWriter for the file and passes it to the closure.' w.writeLine 'It also ensures the stream is flushed and closed after the closure returns.' w.writeLine 'Even if an exception is thrown in the closure.' } assert closure instanceof Closure def file = new File('sample.txt') file.withWriter(closure) Closure is an object
  • 68.
    Calling a closure defsayHello1 = { 'Hello Groovy!' } assert sayHello1() == 'Hello Groovy!' assert sayHello1.call() == 'Hello Groovy!' def sayHello2 = { "Hello $it!" } assert sayHello2('Groovy') == 'Hello Groovy!' assert sayHello2.call('Groovy') == 'Hello Groovy!' def sayHello3 = { name -> "Hello $name!" } assert sayHello3('Groovy') == 'Hello Groovy!' assert sayHello3.call('Groovy') == 'Hello Groovy!'
  • 69.
    Method pointer def sayHello(Stringname) { "Hello $name" } def sayHello(int nTimes) { 'Hello' * nTimes } def pointer = this.&sayHello assert pointer instanceof Closure assert pointer('John') == 'Hello John' assert pointer(3) == 'HelloHelloHello'
  • 70.
    Closure coercion interface Predicate<T>{ boolean accept(T obj) } def filter2 = { it.startsWith 'J' } as Predicate assert filter2.accept('Java') Predicate filter1 = { it.startsWith 'G' } assert filter1.accept('Groovy')
  • 71.
    Methods accepting aSAM type static <T> List<T> filter(List<T> source, Predicate<T> predicate) { source.findAll { predicate.accept(it) } } assert filter(['Java', 'Groovy'], { it.contains 'G' }) == ['Groovy'] assert filter(['Java', 'Groovy']) { it.contains 'G' } == ['Groovy']
  • 72.
    � this The enclosingclass of a closure definition � owner The enclosing object of a closure definition (a class or a closure) � delegate An object where methods calls or properties are resolved if no receiver of the message is defined Delegation
  • 73.
    Example class User { defname } def user = new User(name: 'John') def getUserName = { "The name is $name" } assert getUserName() == 'The name is John' // MissingPropertyException getUserName.delegate = user assert getUserName() == 'The name is John'
  • 74.
    Example (Gradle) apply plugin:'java' apply plugin: 'eclipse' sourceCompatibility = 1.5 version = '1.0' repositories { mavenCentral() } dependencies { compile group: 'commons-collections', name: 'commons-collections', version: '3.2.2' testCompile group: 'junit', name: 'junit', version: '4.+' }
  • 75.
    Closures First class citizensof type groovy.lang.Closure Can access any variables of the surrounding context Support delegation concept Method reference via GroovyClass.&groovyMethod Lambda expressions Instances of SAM type they represent Can access only effectively final variables of the surrounding context Method reference via JavaClass::javaMethod Closures vs Java 8 lambda expressions
  • 76.
  • 77.
    “ Metaprogramming is thewriting of computer programs with the ability to treat programs as their data.
  • 78.
  • 79.
    Runtime metaprogramming POJO Regular Javaobject Class written in Java or other JVM language POGO Class written in Groovy Extends java.lang.Object and implements groovy.lang.GroovyObject GroovyInterceptable All methods intercepted through invokeMethod
  • 80.
  • 81.
    GroovyObject interface public interfaceGroovyObject { Object invokeMethod(String name, Object args); Object getProperty(String propertyName); void setProperty(String propertyName, Object newValue); MetaClass getMetaClass(); void setMetaClass(MetaClass metaClass); }
  • 82.
    invokeMethod class InvokeMethodExample implementsGroovyInterceptable { def existingMethod() { 'existingMethod()' } def invokeMethod(String name, Object args) { def m = metaClass.getMetaMethod(name, *args) if (!m) return "Handle unknown method: $name($args)" return "Intercepted: ${m.invoke(this, *args)}" } } def obj = new InvokeMethodExample() assert obj.existingMethod() == 'Intercepted: existingMethod()' assert obj.newMethod(true) == 'Handle unknown method: newMethod([true])'
  • 83.
    methodMissing class User { defmethodMissing(String name, def args) { if (name.startsWith("findBy")) { def field = name[6].toLowerCase() + name[7..-1] return "Looking for a user by $field=${args.first()}" } else throw new MissingMethodException(name, this.class, args) } } def user = new User() assert user.findByFirstName('John') == 'Looking for a user by firstName=John' assert user.findByAge(30) == 'Looking for a user by age=30'
  • 84.
    Example def writer =new StringWriter() def xml = new MarkupBuilder(writer) xml.movies() { movie(year: 1977) { name('Star Wars: Episode IV') director('George Lucas') } movie(year: 1989) { name('Indiana Jines and the Last Crusade') director('Steven Spielberg') } }
  • 85.
    Example (cont’d) <movies> <movie year='1977'> <name>StarWars: Episode IV</name> <director>George Lucas</director> </movie> <movie year='1989'> <name>Indiana Jines and the Last Crusade</name> <director>Steven Spielberg</director> </movie> </movies>
  • 86.
    MetaClass Defines the behaviourof any Groovy or Java class Client API defined via MetaObjectProtocol Contract with Groovy runtime system Cached in MetaClassRegistry Available via metaClass property
  • 87.
    ExpandoMetaClass Allows for dynamicallyadding/changing Methods Constructors Properties
  • 88.
    Adding/changing methods class User{ String name } User.metaClass.nameInUpperCase = { -> name.toUpperCase() } User.metaClass.nameInLowerCase << { -> name.toLowerCase() } // Has to be a new method def user = new User(name: 'John Smith') assert user.nameInUpperCase() == 'JOHN SMITH' assert user.nameInLowerCase() == 'john smith'
  • 89.
    Adding/changing constructors class User{ String name } User.metaClass.constructor << { String name -> new User(name: name) } def user = new User('John Smith') assert user.name == 'John Smith'
  • 90.
    Adding/changing properties class User{ String name } User.metaClass.age = 30 def user = new User(name: 'John Smith') assert user.age == 30 user.age = 35 assert user.age == 35
  • 91.
    Adding/changing static methods classUser { String name } User.metaClass.static.newInstance << { name -> new User(name: name) } def user = User.newInstance('John Smith') assert user.name == 'John Smith'
  • 92.
    Compile-time metaprogramming Allows codegeneration at compile-time Alters Abstract Syntax Tree (AST) of a program The changes are visible in bytecode Callable from other JVM languages Usually better performance
  • 93.
    AST transformations Global ASTtransformation Applied as soons as they are found on compile classpath No out-of-the-box transformations available Local AST transformation Applied by annotating the source code with markers Supports parameters
  • 94.
    Available AST transformations Reducingboilerplate (code generation) Implementing design patterns Logging Declarative concurrency Cloning Testing ...
  • 95.
    Examples � @groovy.transform.ToString � @groovy.transform.EqualsAndHashCode �@groovy.transform.TupleConstructor � @groovy.transform.Canonical @groovy.transform.InheritConstructors
  • 96.
    Available AST transformations(cont’d) � @groovy.lang.Lazy � @groovy.transform.Sortable � @groovy.transform.builder.Builder � @groovy.lang.Delegate � @groovy.transform.Immutable
  • 98.
  • 99.
    “ It is abouthow fast you can write the code.
  • 100.
    “ If you can,go with a language and tools you already know.
  • 101.
    Strengths Familiar, expressive andsuccinct syntax Smooth Java integration Vibrant and rich ecosystem Rich and easy-to-use API Embedded dependency manager
  • 102.
  • 103.
    “ It is abouthow fast you can understand the code.
  • 104.
    What does thiscode do? def process(def arg1, def arg2, def arg3) { [arg1, arg2, arg3].inject('') { x, y -> if (y <= 0) return "${x}00" if (y > 255) return "${x}ff" def s = '' for (int n = y; n > 0; n = n.intdiv(16)) { int r = n % 16 s = r < 10 ? r + s : "${(char) (((int) 'A') - 10 + r)}$s" } s.length() == 1 ? "${x}0$s" : "$x$s" } }
  • 105.
    And now? String rgbToHex(intr, int g, int b) { [r, g, b].inject('') { acc, val -> if (val <= 0) return "${acc}00" if (val > 255) return "${acc}ff" def hex = '' for (int i = val; i > 0; i = i.intdiv(16)) { int j = i % 16 hex = j < 10 ? j + hex : "${(char) (((int) 'A') - 10 + j)}$hex" } hex.length() == 1 ? "${acc}0$hex" : "$acc$hex" } }
  • 106.
    Best practices Use explicittypes for public APIs Interfaces Document client contract Use descriptive names (for everything) Avoid using the same variable name for different things Use optional elements explicitly if it helps code readability parentheses, return keyword
  • 107.
    Best practices (cont’d) Restrictaccess to members that are not part of API private, protected, @PackageScope Use @TypeChecked or @CompileStatic capabilities if possible Keep your code segments small and isolated Especially if making use of duck typing Cover code with as many test suites as reasonable Refactoring
  • 108.
    “ Groovy is nota replacement for Java, it is addition to Java.
  • 109.
  • 110.
  • 111.
    Thanks! Any questions? You canfind me at petr.giecek.82@gmail.com https://www.linkedin.com/in/petrgiecek https://github.com/pgiecek
  • 112.
    Credits Special thanks toall the people who made and released these awesome resources for free: Presentation template by SlidesCarnival Photographs by Unsplash