Groovy is a dynamic language for the Java Virtual Machine that aims to be a better Java. It builds upon Java's strengths while adding features inspired by languages like Python, Ruby, and Smalltalk. Groovy compiles to Java bytecode, so it can be used anywhere Java can be used. Groovy reduces boilerplate code through features like closures and method missing. It also supports metaprogramming through techniques like metaclasses that allow modifying classes at runtime. Developers must be aware of some differences from Java like in polymorphism and how GStrings are lazily evaluated.
2. Groovy
!=
Java Replacement
Relies on Java
Is slower than Java
Great for Prototypes and Scripting
Strives as an Embedded Language
Ideal for Domain Specific Languages
3. What is Groovy?
builds upon the strengths of Java but has
additional power features inspired by languages
like Python, Ruby and Smalltalk
increases developer productivity by reducing
scaffolding code when developing web, GUI,
database or console applications
seamlessly integrates with all existing Java
classes and libraries
compiles straight to Java bytecode so you can
use it anywhere you can use Java
As described at http://groovy.codehaus.org/
4. Why all of this noise?
import java.io.*;
public class HelloWorld
{
public static void main(String[] args) throws Exception
{
String fileName = "/home/wes/test.txt";
BufferedReader br = new BufferedReader(new FileReader(fileName));
try
{
String line = null;
while((line=br.readLine()) != null)
{
System.out.println(line);
}
}
finally
{
br.close();
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
5. Shouldn't it be Simpler?
new File('/home/wes/test.txt').eachLine { println it }1
Eliminates the boilerplate code associated with Java
Provides convenient shortcuts and expressive syntax
Leverages extensive collection of Java libraries
Closures provides elegant, reusable solutions
Groovy's Strengths are Simple but Powerful
6. Closures are
Anonymous Functions
// define a closure
def myCalc = { numForCalc1,numForCalc2 -> numForCalc1 * numForCalc2 }
// define a method with closure as parameter
def doMyCalc(num1, num2, calculateClosure)
{
calculateClosure(num1,num2)
}
// these are all the same
println myCalc(10,20)
println doMyCalc(10,20,myCalc)
println doMyCalc(10,20,{ a, b -> a * b })
println doMyCalc(10,20) { a, b -> a * b }
// or we could provide alternate implementation of the calculation
println doMyCalc(10,20) { a, b -> a - b }
println doMyCalc(10,20) { a, b -> a + b }
println doMyCalc(10,20) { a, b -> b - a }
println doMyCalc(10,20) { a, b -> a ^ b }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
7. What's Missing? Optional?
Return Statements
last value in a method is an implicit return value
Parameter and Return Types
duck typing is supported
types are checked/enforced when present
Classes
still available for organization purposes
not required as an entry point
Compilation
groovyc can be used to compile groovy to class files
groovy executable provides dynamic runtime
8. Java from Groovy
public class Contact
{
private String name, email, phone;
public String getName() { return name; }
public void setName(String name){ this.name = name; }
public String getEmail() { return email; }
public void setEmail(String email){ this.email = email; }
public String getPhone() { return phone; }
public void setPhone(String phone) { this.phone = phone; }
}
Contact contact = new Contact(name: 'Wes',
email: 'wes@mail.com',
Phone : '123-123-1234');
println "Contact ${contact.name} at ${contact.email} or ${contact.phone}"
1
2
3
4
5
Contact.java
useContact.groovy
10. Metaprogramming
Add behavior dynamically at runtime
Change existing behavior at runtime
Handle missing behavior gracefully
Invoke behavior dynamically
What if the rules only applied when we wanted?
11. MetaClass
def testString1 = "This is a test string."
def testString2 = "This is ALSO a TEST string."
println "Test #1a: ${testString1}" // This is a test string.
println "Test #2a: ${testString2}" // This is ALSO a TEST string.
try
{
testString1.doTestThing()
}
catch(Exception ex)
{
println "Error: ${ex.class.name}" // groovy.lang.MissingMethodException
}
println "Implement doTestThing at the class level"
String.metaClass.doTestThing = { delegate - " test" }
println "Test #1b: ${testString1.doTestThing()}" // This is a string.
println "Test #2b: ${testString2.doTestThing()}" // This is ALSO a TEST string.
println "Implement doTestThing at the instance level"
testString2.metaClass.doTestThing = { delegate - ~"(?i) test" }
println "Test #1c: ${testString1.doTestThing()}" // This is a string.
println "Test #2c: ${testString2.doTestThing()}" // This is ALSO a string.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
12. Intercepting Method Calls
void methodMissing(String methodName, args)
Allows implementation to be provided when method not present
Object getProperty(String propertyName)
Allows interception of get methods when Groovy's object.property syntax used
void setProperty(String propertyName, Object newValue)
Allows interception of set methods when Groovy's object.property syntax used
Object invokeMethod(String methodName, args)
Same as methodMissing when methodMissing not present
Allows interception of all methods when GroovyInterceptable is implemented
13. Method Missing
class LikesItAll
{
def likesSports() { println "I play and watch lots of sports!" }
def likesMovies() { println "I see movies all the time!" }
def everythingElse = { println "I really like ${it} too!" }
def methodMissing(String name, args)
{
if(name =~ /^likes/)
everythingElse(name-"likes")
else
println "What?"
}
}
LikesItAll dude = new LikesItAll()
dude.likesMovies()
dude.likesBooks()
dude.likesSports()
dude.dislikesSomething()
dude.likesCars()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Results:
I see movies all the time!
I really like Books too!
I play and watch lots of sports!
What?
I really like Cars too!
14. GroovyInterceptable
class Bank implements GroovyInterceptable
{
def balance=0
def deposit(amt) { balance += amt }
def withdraw(amt) { balance -= amt }
def invokeMethod(String name, args)
{
def metaMethod = metaClass.getMetaMethod(name, args)
if(metaMethod)
{
metaClass.print "Performing ${name} of $${args[0]}"
return metaMethod.invoke(this, args)
}
metaClass.println "This bank does not support ${name}ing"
}
}
Bank bank = new Bank()
bank.deposit 100
bank.withdraw 20
bank.borrow 30
println "Balance is: $${bank.balance}"
Results:
Performing deposit of $100
Performing withdraw of $20
This bank does not support borrowing
Balance is: $80
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25