...
Ternary Operator ? x : y
This operator is a shortcut used to writing simplify the following syntax:
Code Block |
---|
language | groovy |
---|
title | Case 1: Complicated syntax |
---|
|
if (product.label != null && product.label.length() > 0) {
return product.label
} else {
return 'N/A'
} |
You can rewrite this the above statement using the ternary operator:
Code Block |
---|
title | Case 2: Simplified syntax |
---|
|
return (product.label != null && product.label.length() > 0) ? product.label : 'N/A' |
Or even And then simplify it even more since not-empty check in Groovy can be done like this:
Code Block |
---|
title | Case 3: Simplified syntax |
---|
|
return product.label ? product.label : 'N/A' |
Elvis Operator ?:
This operator is a very useful shortcut to writing thisused to provide a default fallback value if certain variable is empty or null. If the expression in the middle of the ternary operator is identical to its first expression, you should simplify the return statement from the previous example above using ?: operator:
Code Block |
---|
| return product.label ? product.label : 'N/A' // ternary operator
title | Case 1: Preferred syntax |
---|
|
return product.label ?: 'N/A' // Elvis operator |
You can rewrite it like thisThis can be useful if you need, for example, to always return a date when target date is not set:
Code Block |
---|
language | groovy |
---|
title | Case 2: Common example - target date |
---|
|
date = api.targetDate() ?: new Date() // if target date is null, wereturn use the current date |
Or define a default value for a parameter if it is not set:
Code Block |
---|
language | groovy |
---|
title | Case 3: Common example - input parameter adjustment |
---|
|
lookback = api.userEntrydecimalUserEntry('Look-back period (default: 20)') ?: 20 // if the user doesn't specify the look-back period, it will default to 20 |
Info |
---|
In case you wonder why this is called Elvis operator |
...
: ?: → rotate 90° right → Image Modified→ Image Modified |
Safe Navigation Operator ?.
When you check for a null reference every time you want to This operator provides a not-null check before accessing a value from object. Whenever you access object's attribute or method , you might be using something likeyou should check for a non-null reference like this:
Code Block |
---|
language | groovy |
---|
title | Case 1: Functionally correct, but complicated syntax |
---|
|
if (date) {
return date.format('yyyy-MM-dd')
}
return null |
Advanced Groovy allows you Instead of the case above, it is recommended to use the ternary operator instead:
Code Block |
---|
language | groovy |
---|
title | Case 2: Simplified syntax |
---|
|
return date ? date.format('yyyy-MM-dd') : null |
But even better is to use the Safe safe navigation operator:
Code Block |
---|
language | groovy |
---|
title | Case 3: Preffered syntax |
---|
|
return date?.format('yyyy-MM-dd') |
If the date is null, the whole expression will evaluate to return null. You can even chain the callsmultiple occurrences of safe navigation operator:
Code Block |
---|
language | groovy |
---|
title | Case 4: Multiple occurences for safe navigation operator |
---|
|
return product?.attribute21?.toBigDecimal() |
Spread Operator *
Sometimes you may want to pass a collection of objects to a method which accepts a variable name of parameters only. Such as Filter.orImagine the following api.find with many filters:
Code Block |
---|
| def filters = [
| title | Case 1: Functionally correct, but complicated syntax |
---|
|
api.find('P', Filter.equal('attribute5'"attribute1", '"P'"), Filter.or(Filter.equal("attribute2", "Red"), Filter.equal('attribute5'"attribute2", 'M'"Green"), Filter.equal("attribute2", "Blue")),Filter.equal('attribute5', 'U'),
]
// this will throw a compilation error as Filter.or doesn't accept a collection but a variable number of Filter arguments"attribute3", 123)) |
It works fine, but it's very difficult to read right? Therefore the following syntax is preferred:
Code Block |
---|
language | groovy |
---|
title | Case 2: Preferred syntax, easy to read |
---|
|
def filter = Filter.and(
Filter.equal("attribute1", "P"),
Filter.or(
Filter.equal("attribute2", "Red"),
Filter.equal("attribute2", "Green"),
Filter.equal("attribute2", "Blue"),
),
Filter.equal("attribute3", 123),
)
api.find('P', Filter.or(filtersfilter)) |
...
It is now much easier to read what the filter is supposed to return and also distinguish the priorities between AND and OR, right?
Sometimes you may find the following syntax that has identical meaning:
Code Block |
---|
language | groovy |
---|
title | Case 3: Alternative syntax, easy to read |
---|
|
def filters = [
Filter.equal("attribute1", "P"),
Filter.or(
Filter.equal('attribute5'"attribute2", 'P'"Red"),
Filter.equal("attribute2", "Green"),
Filter.equal('attribute5'"attribute2", 'M'"Blue"),
),
Filter.equal('attribute5'"attribute3", 'U'123),
]
// notice the * char. This will unwrap each elementitem of the collection and pass it to the method as a separate parameter
api.find('P', *filters) |
Info |
---|
Note that this syntax is representing AND between all filters - that is the behaviour of api.find(). |
Common mistake is that you forget the * operator in the above case:
Code Block |
---|
language | groovy |
---|
title | Case 4: Wrong syntax - missing * |
---|
|
api.find('P', filters) |
Groovy compiler will throw a compilation error since no version of api.find() accepts a collection as argument but only a variable number of Filter objects:
Code Block |
---|
|
No signature of method: net.pricefx.formulaengine.scripting.SandboxAPI.find() is applicable for argument types: (java.lang.String, java.util.ArrayList) values: [P, [`attribute1` = "P", (`attribute2` = "Red" or `attribute2` = "Green" or(*filters) `attribute2` = "Blue"), ...]]
Possible solutions: find(), find(java.lang.String, [Lcom.googlecode.genericdao.search.Filter;), find(java.lang.String, int, [Lcom.googlecode.genericdao.search.Filter;), find(groovy.lang.Closure), find(java.lang.String, int, java.lang.String, [Lcom.googlecode.genericdao.search.Filter;), uuid() |
Spaceship Operator <=>
This operator delegates to the compareTo
method of the left-side operand and passes it the right-side operand.
...
It is best when mixed with a sort
methodsort
method.
Code Block |
---|
|
def competitors = api.productExtension('Competition')
def sortedByName = competitors .sort { a, b -> return a.attribute1 <=> b.attribute1 } // assuming attribute1 is the name of the competitor |
...