Hidden features of Groovy?
GroovyGroovy Problem Overview
It seems like Groovy was forgotten in this thread so I'll just ask the same question for Groovy.
- Try to limit answers to Groovy core
- One feature per answer
- Give an example and short description of the feature, not just a link to documentation
- Label the feature using bold title as the first line
See also:
Groovy Solutions
Solution 1 - Groovy
Using the spread-dot operator
def animals = ['ant', 'buffalo', 'canary', 'dog']
assert animals.size() == 4
assert animals*.size() == [3, 7, 6, 3]
This is a shortcut for animals.collect { it.size() }
.
Solution 2 - Groovy
The with method allows to turn this:
myObj1.setValue(10)
otherObj.setTitle(myObj1.getName())
myObj1.setMode(Obj1.MODE_NORMAL)
into this
myObj1.with {
value = 10
otherObj.title = name
mode = MODE_NORMAL
}
Solution 3 - Groovy
Using hashes as pseudo-objects.
def x = [foo:1, bar:{-> println "Hello, world!"}]
x.foo
x.bar()
Combined with duck typing, you can go a long way with this approach. Don't even need to whip out the "as" operator.
Solution 4 - Groovy
Anyone know about Elvis?
def d = "hello";
def obj = null;
def obj2 = obj ?: d; // sets obj2 to default
obj = "world"
def obj3 = obj ?: d; // sets obj3 to obj (since it's non-null)
Solution 5 - Groovy
Finding out what methods are on an object is as easy as asking the metaClass:
"foo".metaClass.methods.name.sort().unique()
prints:
["charAt", "codePointAt", "codePointBefore", "codePointCount", "compareTo", "compareToIgnoreCase", "concat", "contains", "contentEquals", "copyValueOf", "endsWith", "equals", "equalsIgnoreCase", "format", "getBytes", "getChars", "getClass", "hashCode", "indexOf", "intern", "lastIndexOf", "length", "matches", "notify", "notifyAll", "offsetByCodePoints", "regionMatches", "replace", "replaceAll", "replaceFirst", "split", "startsWith", "subSequence", "substring", "toCharArray", "toLowerCase", "toString", "toUpperCase", "trim", "valueOf", "wait"]
Solution 6 - Groovy
To intercept missing static methods use the following
Foo {
static A() { println "I'm A"}
static $static_methodMissing(String name, args) {
println "Missing static $name"
}
}
Foo.A() //prints "I'm A"
Foo.B() //prints "Missing static B"
-Ken
Solution 7 - Groovy
Destructuring
It might be called something else in Groovy; it's called destructuring in clojure. You'll never believe how handy it can come.
def list = [1, 'bla', false]
def (num, str, bool) = list
assert num == 1
assert str == 'bla'
assert !bool
Solution 8 - Groovy
For testing java code with groovy, object graph builder is amazing:
def company = builder.company( name: 'ACME' ) {
address( id: 'a1', line1: '123 Groovy Rd', zip: 12345, state: 'JV' )
employee( name: 'Duke', employeeId: 1 ){
address( refId: 'a1' )
}
}
Standard feature, but still really nice.
(You do need to give any properties of your POJO that are List
s a default value of an empty list rather than null
for builder support to work.)
Solution 9 - Groovy
println
"""
Groovy has "multi-line" strings.
Hooray!
"""
Solution 10 - Groovy
Unlike Java, in Groovy, anything can be used in a switch statement, not just primitive types. In a typical eventPerformed method
switch(event.source) {
case object1:
// do something
break
case object2:
// do something
break
}
Solution 11 - Groovy
In groovy 1.6, regular expressions work with all of the closure iterators (like each, collect, inject, etc) and allow you to easily work with the capture groups:
def filePaths = """
/tmp/file.txt
/usr/bin/dummy.txt
"""
assert (filePaths =~ /(.*)\/(.*)/).collect { full, path, file ->
"$file -> $path"
} == ["file.txt -> /tmp", "dummy.txt -> /usr/bin"]
Solution 12 - Groovy
Using the Spaceship Operator
I like the Spaceship operator, useful for all sorts of custom sorting scenarios. Some examples of usage are here. One situation in which it's particularly helpful is in creating a comparator on the fly of an object using multiple fields. e.g.
def list = [ [ id:0, first: 'Michael', last: 'Smith', age: 23 ],
[ id:1, first: 'John', last: 'Smith', age: 30 ],
[ id:2, first: 'Michael', last: 'Smith', age: 15 ],
[ id:3, first: 'Michael', last: 'Jones', age: 15 ],
]
// sort list by last name, then first name, then by descending age
assert (list.sort { a,b -> a.last <=> b.last ?: a.first <=> b.first ?: b.age <=> a.age })*.id == [ 3,1,0,2 ]
Solution 13 - Groovy
Closures can make all the old try-finally games of resource management go away. The file stream is automatically closed at the end of the block:
new File("/etc/profile").withReader { r ->
System.out << r
}
Solution 14 - Groovy
The features provided by the transformations inside the GDK's groovy.transform
package, such as:
@Immutable
: The @Immutable annotation instructs the compiler to execute an AST transformation which adds the necessary getters, constructors, equals, hashCode and other helper methods that are typically written when creating immutable classes with the defined properties.@CompileStatic
: This will let the Groovy compiler use compile time checks in the style of Java then perform static compilation, thus bypassing the Groovy meta object protocol.@Canonical
: The @Canonical annotation instructs the compiler to execute an AST transformation which adds positional constructors, equals, hashCode and a pretty print toString to your class.
Others:
-
@Slf4j
This local transform adds a logging ability to your program using LogBack logging. Every method call on a unbound variable named log will be mapped to a call to the logger. -
Groovy's XML Slurper: easy parsing of XML. Killer feature!
Solution 15 - Groovy
Closure-Based Interface Implementation
If you have a typed reference such as:
MyInterface foo
You can implement the entire interface using:
foo = {Object[] args -> println "This closure will be called by ALL methods"} as MyInterface
Alternatively, if you want to implement each method separately, you can use:
foo = [bar: {-> println "bar invoked"},
baz: {param1 -> println "baz invoked with param $param1"}] as MyInterface
Solution 16 - Groovy
You can convert a list to a map by using toSpreadMap(), convenient at times when the order in the list is enough to determine the keys and the values associated with them. See example below.
def list = ['key', 'value', 'foo', 'bar'] as Object[]
def map = list.toSpreadMap()
assert 2 == map.size()
assert 'value' == map.key
assert 'bar' == map['foo']
Solution 17 - Groovy
Remove null
values from list
def list = [obj1, obj2, null, obj4, null, obj6]
list -= null
assert list == [obj1, obj2, obj4, obj6]
Solution 18 - Groovy
class Foo {
def footest() { return "footest"}
}
class Bar {
@Delegate Foo foo = new Foo()
}
def bar = new Bar()
assert "footest" == bar.footest()
Solution 19 - Groovy
I know I'am a bit late but I think there are some nice features missing here:
Collection plus/minus operators
def l = [1, 2, 3] + [4, 5, 6] - [2, 5] - 3 + (7..9)
assert l == [1, 4, 6, 7, 8, 9]
def m = [a: 1, b: 2] + [c: 3] - [a: 1]
assert m == [b: 2, c: 3]
Switch statement
switch (42) {
case 0: .. break
case 1..9: .. break
case Float: .. break
case { it % 4 == 0 }: .. break
case ~/\d+/: .. break
}
Ranges and indexing
assert (1..10).step(2) == [1, 3, 5, 7, 9]
assert (1..10)[1, 4..8] == [2, 5, 6, 7, 8, 9]
assert ('a'..'g')[-4..-2] == ['d', 'e', 'f']
Unicode variable names
def α = 123
def β = 456
def Ω = α * β
assert Ω == 56088
Solution 20 - Groovy
Underscore in literals
When writing long literal numbers, it’s harder on the eye to figure out how some numbers are grouped together, for example with groups of thousands, of words, etc. By allowing you to place underscore in number literals, it’s easier to spot those groups:
long creditCardNumber = 1234_5678_9012_3456L
long socialSecurityNumbers = 999_99_9999L
double monetaryAmount = 12_345_132.12
long hexBytes = 0xFF_EC_DE_5E
long hexWords = 0xFFEC_DE5E
long maxLong = 0x7fff_ffff_ffff_ffffL
long alsoMaxLong = 9_223_372_036_854_775_807L
long bytes = 0b11010010_01101001_10010100_10010010
Solution 21 - Groovy
Argument reordering with implicit arguments is another nice one.
This code:
def foo(Map m=[:], String msg, int val, Closure c={}) {
[...]
}
Creates all these different methods:
foo("msg", 2, x:1, y:2)
foo(x:1, y:2, "blah", 2)
foo("blah", x:1, 2, y:2) { [...] }
foo("blah", 2) { [...] }
And more. It's impossible to screw up by putting named and ordinal arguments in the wrong order/position.
Of course, in the definition of "foo", you can leave off "String" and "int" from "String msg" and "int val" -- I left them in just for clarity.
Solution 22 - Groovy
I think it's a combination of closures as parameter and parameter-default-values:
public void buyItems(Collection list, Closure except={it > 0}){
list.findAll(){except(it)}.each(){print it}
}
buyItems([1,2,3]){it > 2}
buyItems([0,1,2])
prints: "312"
Solution 23 - Groovy
Using the spread operator in method parameters
This is a great help when converting code to data:
def exec(operand1,operand2,Closure op) {
op.call(operand1,operand2)
}
def addition = {a,b->a+b}
def multiplication = {a,b->a*b}
def instructions = [ [1,2,addition],
[2,2,multiplication]
]
instructions.each{instr->
println exec(*instr)
}
Also helpful is this usage:
String locale="en_GB"
//this invokes new Locale('en','GB')
def enGB=new Locale(*locale.split('_'))
Solution 24 - Groovy
Memoization
Memoization is an optimization technique that consists in storing the results of expensive function calls and returning the cached result whenever the function is called again with the same arguments.
There is an unlimited version, that will cache ever pair of (input arguments, return value) it will ever see; and a limited version, that will cache the last N input arguments seen and their results, using a LRU cache.
Memoization of methods:
import groovy.transform.Memoized
@Memoized
Number factorial(Number n) {
n == 0 ? 1 : factorial(n - 1)
}
@Memoized(maxCacheSize=1000)
Map fooDetails(Foo foo) {
// call expensive service here
}
Memoization of closures:
def factorial = {Number n ->
n == 0 ? 1 : factorial(n - 1)
}.memoize()
fooDetails = {Foo foo ->
// call expensive service here
}.memoizeAtMost(1000)
The Wikipedia page has extensive information on the uses of Memoization in Computer Science. I will just point out one simple practical use.
Deferring the initialization of a constant to the last possible moment
Sometimes you have a constant value that cannot be initialized at class definition or creation time. For example, the constant expression may make use of another constant or a method from a different class, which will be plugged in by something else (Spring or such) after the initialization of your class.
In this case, you can convert your constant into a getter and decorate it with @Memoized
. It will only be computed once, the first time it's accessed, and then the value cached and reused:
import groovy.transform.Memoized
@Memoized
def getMY_CONSTANT() {
// compute the constant value using any external services needed
}
Solution 25 - Groovy
Groovy can work a lot like Javascript. You can have private vars and functions via closure. You can also curry functions with closures.
class FunctionTests {
def privateAccessWithClosure = {
def privVar = 'foo'
def privateFunc = { x -> println "${privVar} ${x}"}
return {x -> privateFunc(x) }
}
def addTogether = { x, y ->
return x + y
}
def curryAdd = { x ->
return { y-> addTogether(x,y)}
}
public static void main(String[] args) {
def test = new FunctionTests()
test.privateAccessWithClosure()('bar')
def curried = test.curryAdd(5)
println curried(5)
}
}
output:
foo bar 10
Solution 26 - Groovy
Dynamic method invocation
You can invoke a method using a string with its name
class Dynamic {
def one() { println "method one()" }
def two() { println "method two()" }
}
def callMethod( obj, methodName ) {
obj."$methodName"()
}
def dyn = new Dynamic()
callMethod( dyn, "one" ) //prints 'method one()'
callMethod( dyn, "two" ) //prints 'method two()'
dyn."one"() //prints 'method one()'
Solution 27 - Groovy
How to build a JSON tree in a couple of lines in groovy ?
-
define your tree with self-referential
withDefault
closuredef tree // declare first before using a self reference tree = { -> [:].withDefault{ tree() } }
-
Create your own JSON tree
frameworks = tree() frameworks.grails.language.name = 'groovy' frameworks.node.language.name = 'js'
def result = new groovy.json.JsonBuilder(frameworks)
Which gives: {"grails":{"language":{"name":"groovy"}},"node":{"language":{"name":"js"}}}
Solution 28 - Groovy
Safe navigation operator
The Safe Navigation operator is used to avoid a NullPointerException. Typically when you have a reference to an object you might need to verify that it is not null before accessing methods or properties of the object. To avoid this, the safe navigation operator will simply return null instead of throwing an exception, like so:
def person = Person.find { it.id == 123 } // find will return a null instance
def name = person?.name // use of the null-safe operator prevents from a NullPointerException, result is null
Solution 29 - Groovy
Multiple variables deceleration
1)Multiple variables declarations in single line
def (a,b,c) = [1,2,3]
2)Using different type declarations.
def (String a, int b) = ['Groovy', 1]
Solution 30 - Groovy
Elvis operator
"Elvis operator" is a shortening of the ternary operator. One instance of where this is handy is for returning a 'sensible default' value if an expression resolves to false(as in Groovy truth). A simple example might look like this:
with the ternary operator, you have to repeat the value you want to assign
displayCity = user.city ? user.city: 'UnKnown City'
with the Elvis operator, the value, which is tested, is used if it is not false
displayCity = user.city ?: 'UnKnown City'
Solution 31 - Groovy
Coercion operator
The coercion operator (as) is a variant of casting. Coercion converts object from one type to another without them being compatible for assignment. Let’s take an example:
Integer x = 123
String s = (String) x
Integer is not assignable to a String, so it will produce a ClassCastException at runtime
This can be fixed by using coercion instead:
Integer x = 123
String s = x as String
Integer is not assignable to a String, but use of as will coerce it to a String