Hidden features of Groovy?

Groovy

Groovy 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:

  1. Hidden features of Python
  2. Hidden features of Ruby
  3. Hidden features of Perl
  4. Hidden features of Java

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.

ObjectGraphBuilder

(You do need to give any properties of your POJO that are Lists 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

@Delegate

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 ?

  1. define your tree with self-referential withDefault closure

    def tree // declare first before using a self reference tree = { -> [:].withDefault{ tree() } }

  2. 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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionanonView Question on Stackoverflow
Solution 1 - GroovyPaul KingView Answer on Stackoverflow
Solution 2 - GroovyRui VieiraView Answer on Stackoverflow
Solution 3 - GroovyRobert FischerView Answer on Stackoverflow
Solution 4 - GroovybilljamesdevView Answer on Stackoverflow
Solution 5 - GroovyTed NaleidView Answer on Stackoverflow
Solution 6 - GroovykenView Answer on Stackoverflow
Solution 7 - GroovyBojan DolinarView Answer on Stackoverflow
Solution 8 - GroovykrosenvoldView Answer on Stackoverflow
Solution 9 - GroovyJohn FlinchbaughView Answer on Stackoverflow
Solution 10 - GroovyRui VieiraView Answer on Stackoverflow
Solution 11 - GroovyTed NaleidView Answer on Stackoverflow
Solution 12 - GroovymmigdolView Answer on Stackoverflow
Solution 13 - GroovyJohn FlinchbaughView Answer on Stackoverflow
Solution 14 - GroovyHans WesterbeekView Answer on Stackoverflow
Solution 15 - GroovyDónalView Answer on Stackoverflow
Solution 16 - GroovykenView Answer on Stackoverflow
Solution 17 - GroovyBIdesiView Answer on Stackoverflow
Solution 18 - GroovyOmnipresentView Answer on Stackoverflow
Solution 19 - GroovymichaView Answer on Stackoverflow
Solution 20 - GroovyPankaj ShindeView Answer on Stackoverflow
Solution 21 - GroovyRobert FischerView Answer on Stackoverflow
Solution 22 - GroovyGrimView Answer on Stackoverflow
Solution 23 - GroovyLuis MuñizView Answer on Stackoverflow
Solution 24 - GroovyTobiaView Answer on Stackoverflow
Solution 25 - GroovyJasonView Answer on Stackoverflow
Solution 26 - GroovylifeisfooView Answer on Stackoverflow
Solution 27 - Groovyludo_rjView Answer on Stackoverflow
Solution 28 - GroovyPankaj ShindeView Answer on Stackoverflow
Solution 29 - GroovyZulqarnain SattiView Answer on Stackoverflow
Solution 30 - GroovyIshworView Answer on Stackoverflow
Solution 31 - GroovyIshworView Answer on Stackoverflow