blob: 25f96d6c47cdd5c28887beeb3f901c20d68225ca [file] [log] [blame]
The 1.8 release of Groovy comes with many new features that greatly
enhance
* the dynamic expressiveness of Groovy, specifically for defining DSLs
* runtime performance
* concurrent and parallel execution
* design by contract
* functional programming style
* first-class JSON support
* compile-time meta programming
* and more helpers and library additions
These features have undergone the Groovy developer process with formal
descriptions, discussion, and voting (GEP - Groovy Enhancement Proposal)
for core parts and less formal developer discussions and JIRA voting for
additional parts.
Our goal has stayed the same, though: to give the Java developer a tool
that makes him more productive, allows him to achieve his goals faster
and with a smaller margin of error, and extend the scalability of the
Java platform from full-blown enterprise projects to everyday "getting
things done" tasks.
[[Groovy18releasenotes-CommandchainsfornicerDomain-SpecificLanguages]]
== Command chains for nicer Domain-Specific Languages
Thanks to its flexible syntax and its compile-time and runtime
metaprogramming capabilities, Groovy is well known for its
Domain-Specific Language capabilities. However, we felt that we could
improve upon the syntax further by removing additional punctuation
symbols when users chain method calls. This allows DSL implementors to
develop command descriptions that read almost like natural sentences.
Before Groovy 1.8, we could omit parentheses around the arguments of a
method call for top-level statements. But we couldnt chain method
calls. The new `command chain` feature allows us to chain such
parentheses-free method calls, requiring neither parentheses around
arguments, nor dots between the chained calls. The general idea is that
a call like `a b c d` will actually be equivalent to `a(b).c(d)`. This
also works with multiple arguments, closure arguments, and even named
arguments. Furthermore, such command chains can also appear on the
right-hand side of assignments. Lets have a look at some examples
supported by this new syntax:
[source,groovy]
---------------------------------------------------------------------------------------------------------------
turn left then right // equivalent to: turn(left).then(right)
take 2.pills of chloroquinine after 6.hours // equivalent to: take(2.pills).of(chloroquinine).after(6.hours)
paint wall with red, green and yellow // equivalent to: paint(wall).with(red, green).and(yellow)
// with named parameters too
check that: margarita tastes good // equivalent to: check(that: margarita).tastes(good)
// with closures as parameters
given { } when { } then { } // equivalent to: given({}).when({}).then({})
---------------------------------------------------------------------------------------------------------------
It is also possible to use methods in the chain which take no arguments,
but in that case, the parentheses are needed:
[source,groovy]
-------------------------------------------------------------------------------------------------
select all unique() from names // equivalent to: select(all).unique().from(names)
-------------------------------------------------------------------------------------------------
If your command chain contains an odd number of elements, the chain will
be composed of method / arguments, and will finish by a final property
access:
[source,groovy]
-------------------------------------------------------------------------------------
take 3 cookies // equivalent to: take(3).cookies
// and also this: take(3).getCookies()
-------------------------------------------------------------------------------------
This new command chain approach opens up interesting possibilities in
terms of the much wider range of DSLs which can now be written in
Groovy. This new feature has been developed thanks to the Google Summer
of Code program, where our student, Lidia, helped us modify the Groovy
Antlr grammar to extend top-level statements to accept that command
chain syntax.
The above examples illustrate using a command chain based DSL but not
how to create one. You will be able to find some
http://groovyconsole.appspot.com/tag/gep3[further examples] of `command chains`
on the Groovy Web Console but to illustrate creating such a
DSL, we will show just a couple of examples - first using maps and
Closures:
[source,groovy]
------------------------------------------------------------------------------------------------------
show = { println it }
square_root = { Math.sqrt(it) }
def please(action) {
[the: { what ->
[of: { n -> action(what(n)) }]
}]
}
please show the square_root of 100 // equivalent to: please(show).the(square_root).of(100)
// ==> 10.0
------------------------------------------------------------------------------------------------------
Or if you prefer Japanese and a metaprogramming style (see
http://d.hatena.ne.jp/uehaj/20100919/1284906117[here] for more details):
[source,groovy]
----------------------------------------------------------
// Japanese DSL using GEP3 rules
Object.metaClass.を =
Object.metaClass.の = clos -> clos(delegate) }
まず = it }
表示する = println it }
平方根 = Math.sqrt(it) }
まず 100 の 平方根 を 表示す // First, show the square root of 100
// => 10.0
----------------------------------------------------------
As a second example, consider how you might write a DSL for simplifying
one of your existing APIs. Maybe you need to put this code in front of
customers, business analysts or testers who might be not hard-core Java
developers. Well use the `Splitter` from the Google
http://code.google.com/p/guava-libraries/[Guava libraries] project as it
already has a nice Fluent API. Here is how we might use it out of the
box:
[source,groovy]
----------------------------------------------------------------------------------------------------------------
@Grab('com.google.guava:guava:r09')
import com.google.common.base.*
def result = Splitter.on(',').trimResults(CharMatcher.is('_' as char)).split("_a ,_b_ ,c__").iterator().toList()
assert result == ['a ', 'b_ ', 'c']
----------------------------------------------------------------------------------------------------------------
It reads fairly well for a Java developer but if that is not your target
audience or you have many such statements to write, it could be
considered a little verbose. Again, there are many options for writing a
DSL. Well keep it simple with Maps and Closures. Well first write a
helper method:
[source,groovy]
------------------------------------------------------------------------------------------------------
def split(string) {
[on: { sep ->
[trimming: { trimChar ->
Splitter.on(sep).trimResults(CharMatcher.is(trimChar as char)).split(string).iterator().toList()
}]
}]
}
------------------------------------------------------------------------------------------------------
now instead of this line from our original example:
[source,groovy]
----------------------------------------------------------------------------------------------------------------
def result = Splitter.on(',').trimResults(CharMatcher.is('_' as char)).split("_a ,_b_ ,c__").iterator().toList()
----------------------------------------------------------------------------------------------------------------
we can write this:
[source,groovy]
-----------------------------------------------------
def result = split "_a ,_b_ ,c__" on ',' trimming '_'
-----------------------------------------------------
[[Groovy18releasenotes-Performanceimprovements]]
== Performance improvements
Groovys flexible metaprogramming model involves numerous decision
points when making method calls or accessing properties to determine
whether any metaprogamming hooks are being utilized. During complex
expression calculations, such decision points involved identical checks
being executed numerous times. Recent performance improvements allow
some of these checks to be bypassed during an expression calculation
once certain initial assumptions have been checked. Basically if certain
preconditions hold, some streamlining can take place.
Groovy 1.8.0 contains two main streams of optimization work:
* There are several optimizations for basic operations on integers like
plus, minus, multiply, increment, decrement and comparisons. This
version doesnt support the mixed usage of different types. If an
expression contains different types, then it falls back to the classical
way of performing the operation, i.e. no streamlining occurs.
* There is also an optimization for direct method calls. Such a method
call is done directly if it is done on `this` and if the argument
types are a direct match with the parameter types of the method we may
call. Since this is an operation that does not behave too well with a
method call logic based on runtime types we select only methods where
the primitive types match, the parameter types are final or for methods
that take no arguments. Currently methods with a variable parameter list
are not matched in general, unless a fitting array is used for the
method call.
Those two areas of optimization are only the beginning of further
similar improvements. Upcoming versions of the Groovy 1.8.x branch will
see more optimizations coming. In particular, primitive types other than
integers should be expected to be supported shortly.
[[Groovy18releasenotes-GParsbundledwithintheGroovydistribution]]
== GPars bundled within the Groovy distribution
The http://gpars.github.io[GPars] project offers developers new
intuitive and safe ways to handle Java or Groovy tasks concurrently,
asynchronously, and distributed by utilizing the power of the Java
platform and the flexibility of the Groovy language. Groovy 1.8 now
bundles GPars 0.11 in the libraries of the Groovy installation, so that
you can leverage all the features of the library for +
Fork/Join, Map/Filter/Reduce, DataFlow, Actors, Agents, and more with
all the Groovy goodness.
To learn more about GPars, head over to the
http://gpars.github.io/[GPars website], read the
http://gpars.org/guide/index.html[detailed online user guide], or check
out chapter 17 of http://www.manning.com/koenig2[Groovy in Action, 2nd
Edition (MEAP)].
[[Groovy18releasenotes-Closureenhancements]]
== Closure enhancements
Closures are a central and essential piece of the Groovy programming
language and are used in various ways throughout the Groovy APIs. In
Groovy 1.8, we introduce the ability to use closures as annotation
parameters. Closures are also a key part of what gives Groovy its
functional flavor.
[[Groovy18releasenotes-Closureannotationparameters]]
=== Closure annotation parameters
In Java, theres a limited set of types you can use as annotation
parameters (String, primitives, annotations, classes, and arrays of
these). But in Groovy 1.8, were going further and let you use closures
as annotation parameters which are actually transformed into a class
parameter for compatibility reasons.
[source,groovy]
------------------------------------------------
import java.lang.annotation.*
@Retention(RetentionPolicy.RUNTIME)
@interface Invariant {
Class value() // will hold a closure class
}
@Invariant({ number >= 0 })
class Distance {
float number
String unit
}
def d = new Distance(number: 10, unit: "meters")
def anno = Distance.getAnnotation(Invariant)
def check = anno.value().newInstance(d, d)
assert check(d)
------------------------------------------------
Closure annotation parameters open up some interesting possibilities for
framework authors! As an example, the
https://github.com/andresteingress/gcontracts/wiki/[GContracts] project,
which brings the `Design by Contract` paradigm to Groovy makes heavy
use of annotation parameters to allow preconditions, postconditions and
invariants to be declared.
[[Groovy18releasenotes-Closurefunctionalflavors]]
=== Closure functional flavors
[[Groovy18releasenotes-Closurecomposition]]
==== Closure composition
If you recall your math lessons, function composition may be a concept
youre familiar with. And in turn, *Closure composition* is about that:
the ability to compose Closures together to form a new Closure which
chains the call of those Closures. Heres an example of composition in
action:
[source,groovy]
---------------------------------------------
def plus2  = { it 2 }
def times3 = { it 3 }
def times3plus2 = plus2 << times3
assert times3plus2(3) == 11
assert times3plus2(4) == plus2(times3(4))
def plus2times3 = times3 << plus2
assert plus2times3(3) == 15
assert plus2times3(5) == times3(plus2(5))
// reverse composition
assert times3plus2(3) == (times3 >> plus2)(3)
---------------------------------------------
To see more examples of Closure composition and reverse composition,
please have a look at our
https://github.com/apache/groovy/blob/master/src/test/groovy/ClosureComposeTest.groovy[test
case].
[[Groovy18releasenotes-Closuretrampoline]]
==== Closure trampoline
When writing recursive algorithms, you may be getting the infamous stack
overflow exceptions, as the stack starts to have a too high depth of
recursive calls. An approach that helps in those situations is by using
Closures and their new
http://en.wikipedia.org/wiki/Tail_call#Through_trampolining[trampoline]
capability.
Closures are wrapped in a `TrampolineClosure`. Upon calling, a
trampolined Closure will call the original Closure waiting for its
result. If the outcome of the call is another instance of a
`TrampolineClosure`, created perhaps as a result to a call to the
`trampoline()` method, the Closure will again be invoked. This
repetitive invocation of returned trampolined Closures instances will
continue until a value other than a trampolined Closure is returned.
That value will become the final result of the trampoline. That way,
calls are made serially, rather than filling the stack.
Heres an example of the use of `trampoline()` to implement the
factorial function:
[source,groovy]
------------------------------------------------------------------
def factorial
factorial = { int n, def accu = 1G ->
if (n < 2) return accu
factorial.trampoline(n - 1, n * accu)
}
factorial = factorial.trampoline()
assert factorial(1) == 1
assert factorial(3) == 1 * 2 * 3
assert factorial(1000) == 402387260... // plus another 2560 digits
------------------------------------------------------------------
[[Groovy18releasenotes-Closurememoization]]
==== Closure memoization
Another improvement to Closures is the ability to
http://en.wikipedia.org/wiki/Memoization[memoize] the outcome of
previous (ideally side-effect free) invocations of your Closures. The
return values for a given set of Closure parameter values are kept in a
cache, for those memoized Closures. That way, if you have an expensive
computation to make that takes seconds, you can put the return value in
cache, so that the next execution with the same parameter will return
the same result again, we assume results of an invocation are the same
given the same set of parameter values.
There are three forms of memoize functions:
* the standard `memoize()` which caches all the invocations
* `memoizeAtMost(max)` call which caches a maximum number of invocations
* `memoizeAtLeast(min)` call which keeps at least a certain number of
invocation results
* and `memoizeBetween(min, max)` which keeps a range results (between a
minimum and a maximum)
Lets illustrate that:
[source,groovy]
--------------------------------------------------
def plus = { a, b -> sleep 1000; a + b }.memoize()
assert plus(1, 2) == 3 // after 1000ms
assert plus(1, 2) == 3 // return immediately
assert plus(2, 2) == 4 // after 1000ms
assert plus(2, 2) == 4 // return immediately
 
// other forms:
// at least 10 invocations cached
def plusAtLeast = { ... }.memoizeAtLeast(10)
// at most 10 invocations cached
def plusAtMost = { ... }.memoizeAtMost(10)
// between 10 and 20 invocations cached
def plusAtLeast = { ... }.memoizeBetween(10, 20)
--------------------------------------------------
[[Groovy18releasenotes-Curryingimprovements]]
=== Currying improvements
Currying improvements have also been backported to recent releases of
Groovy 1.7, but its worth outlining here for reference. Currying used
to be done only from left to right, but its also possible to do it from
right to left, or from a given index, as the following examples
demonstrate:
[source,groovy]
------------------------------------------
// right currying
def divide = { a, b -> a / b }
def halver = divide.rcurry(2)
assert halver(8) == 4
 
// currying n-th parameter
def joinWithSeparator = { one, sep, two ->
one + sep + two
}
def joinWithComma =
joinWithSeparator.ncurry(1, ', ')
assert joinWithComma('a', 'b') == 'a, b'
------------------------------------------
[[Groovy18releasenotes-NativeJSONsupport]]
== Native JSON support
With the ubiquity of JSON as an interchange format for our applications,
it is natural that Groovy added support for JSON, in a similar fashion
as the support Groovys always had with XML. So Groovy 1.8 introduces a
JSON builder and parser.
[[Groovy18releasenotes-ReadingJSON]]
=== Reading JSON
A `JsonSlurper` class allows you to parse JSON payloads, and access the
nested Map and List data structures representing that content. JSON
objects and arrays are indeed simply represented as Maps and Lists,
giving you access to all the GPath expression benefits
(subscript/property notation, find/findAll/each/inject/groupBy/etc.).
Heres an example showing how to find all the recent commit messages on
the Grails project:
[source,groovy]
--------------------------------------------------------------------------------------------------
import groovy.json.*
def payload = new URL("http://github.com/api/v2/json/commits/list/grails/grails-core/master").text
def slurper = new JsonSlurper()
def doc = slurper.parseText(payload)
doc.commits.message.each { println it }
--------------------------------------------------------------------------------------------------
If you want to see some more examples of the usage of the JSON parser,
you can have a look at the
https://github.com/apache/groovy/blob/master/subprojects/groovy-json/src/test/groovy/groovy/json/JsonSlurperTest.groovy[JsonSlurper
tests] in our code base.
[[Groovy18releasenotes-JSONbuilder]]
=== JSON builder
Parsing JSON data structures is one thing, but we should also be able to
produce JSON content just like we create markup with the
`MarkupBuilder`. The following example:
[source,groovy]
----------------------------
import groovy.json.*
def json = new JsonBuilder()
json.person {
name "Guillaume"
age 33
pets "Hector", "Felix"
}
println json.toString()
----------------------------
Will create the JSON output:
[source,groovy]
------------------------------------------------------------------
{"person":{"name":"Guillaume","age":33,"pets":["Hector","Felix"]}}
------------------------------------------------------------------
You can find some more usages of the JSON builder in our
https://github.com/apache/groovy/blob/master/subprojects/groovy-json/src/test/groovy/groovy/json/JsonBuilderTest.groovy[JsonBuilder
tests].
[[Groovy18releasenotes-PrettyprintingJSONcontent]]
=== Pretty printing JSON content
When given a JSON data structure, you may wish to pretty-print it, so
that you can more easily inspect it, with a more friendly layout. So for
instance, if you want to pretty print the result of the previous
example, you could do:
[source,groovy]
--------------------------------------------------------------------------------------------------------------------
import groovy.json.*
println JsonOutput.prettyPrint('''{"person":{"name":"Guillaume","age":33,"pets":["Hector","Felix"]}}''')​​​​​​​​​​​​
--------------------------------------------------------------------------------------------------------------------
Which would result in the following pretty-printed output:
[source,groovy]
----------------------------
{
"person": {
"name": "Guillaume",
"age": 33,
"pets": [
"Hector",
"Felix"
]
}
}
----------------------------
[[Groovy18releasenotes-NewASTTransformations]]
== New AST Transformations
The Groovy compiler reads the source code, builds an Abstract Syntax
Tree (AST) from it, and then puts the AST into bytecode. With AST
transformations, the programmer can hook into this process. A general
description of this process, an exhaustive description of all available
transformations, and a guide of how to write you own ones can be found
for example in http://www.manning.com/koenig2[Groovy in Action, 2nd
Edition (MEAP)], chapter 9.
Below is a list of all new transformations that come with Groovy 1.8.
They save you from writing repetitive code and help avoiding common
errors.
[[Groovy18releasenotes-Log]]
=== @Log
You can annotate your classes with the @Log transformation to
automatically inject a logger in your Groovy classes, under the `log`
property. Four kind of loggers are actually available:
* `@Log` for java.util.logging
* `@Commons` for Commons-Logging
* `@Log4j` for Log4J
* `@Slf4j` for SLF4J
Heres a sample usage of the @Log transformation:
[source,groovy]
----------------------------------
import groovy.util.logging.*
@Log
class Car {
Car() {
log.info 'Car constructed'
}
}
def c = new Car()
----------------------------------
You can change the name of the logger by specifying a different name,
for instance with `@Log('myLoggerName')`.
Another particularity of these logger AST transformations is that they
take care of wrapping and safe-guarding logger calls with the usual
`isSomeLevelEnabled()` calls. So when you write
`log.info 'Car constructed'`, the generated code is actually equivalent
to:
[source,groovy]
---------------------------------
if (log.isLoggable(Level.INFO)) {
log.info 'Car constructed'
}
---------------------------------
[[Groovy18releasenotes-Field]]
=== @Field
When defining variables in a script, those variables are actually local
to the scripts run method, so they are not accessible from other
methods of the script. A usual approach to that problem has been to
store variables in the binding, by not defining those variables and by
just assigning them a value. Fortunately, the `@Field` transformation
provides a better alternative: by annotating your variables in your
script with this annotation, the annotated variable will become a
private field of the script class.
More concretely, youll be able to do as follows:
[source,groovy]
---------------------------
@Field List awe = [1, 2, 3]
def awesum() { awe.sum() }
assert awesum() == 6
---------------------------
[[Groovy18releasenotes-PackageScopeenhancements]]
=== @PackageScope enhancements
The @PackageScope annotation can be placed on classes, methods or fields
and is used for turning off Groovys visibility conventions and
reverting back to Java conventions. This ability is usually only needed
when using 3rd party libraries which rely on the package scope
visibility. When adding the `@PackageScope` annotation to a field,
Groovy will assign package scope access to the field rather than
automatically treating it as a property (and adding setters/getters).
Annotating a class or method with `@PackageScope` will cause Groovy to
revert to Javas convention of leaving the class/method as package
scoped rather than automatically promoting it to public scope. The class
variant can also take one or more parameters to allow nested setting of
visibility of attributes within the class - see the Javadoc for more
details. Recent releases of Groovy 1.7 have had a more limited version
of this annotation.
[[Groovy18releasenotes-AutoClone]]
=== @AutoClone
The `@AutoClone` annotation is placed on classes which you want to be
`Cloneable`. The annotation instructs the compiler to execute an AST
transformation which adds a public `clone()` method and adds `Cloneable`
to the classes implements list of interfaces. Because the JVM doesnt
have a one-size-fits-all cloning strategy, several customizations exist
for the cloning implementation:
* By default, the `clone()` method will call `super.clone()` before
calling `clone()` on each `Cloneable` property of the class. Example
usage:
[source,groovy]
---------------------------------
import groovy.transform.AutoClone
@AutoClone
class Person {
String first, last
List favItems
Date since
}
---------------------------------
Which will create a class of the following form:
[source,groovy]
-------------------------------------------------------------
class Person implements Cloneable {
...
public Object clone() throws CloneNotSupportedException {
Object result = super.clone()
result.favItems = favItems.clone()
result.since = since.clone()
return result
}
...
}
-------------------------------------------------------------
* Another popular cloning strategy is known as the copy constructor
pattern. If any of your fields are `final` and `Cloneable` you should
set `style=COPY_CONSTRUCTOR` which will then use the copy constructor
pattern.
* As a final alternative, if your class already implements the
`Serializable` or `Externalizable` interface, you might like to set
`style=SERIALIZATION` which will then use serialization to do the
cloning.
See the Javadoc for `AutoClone` for further details.
[[Groovy18releasenotes-AutoExternalizable]]
=== @AutoExternalizable
The `@AutoExternalizable` class annotation is used to assist in the
creation of `Externalizable` classes. The annotation instructs the
compiler to execute an AST transformation which adds `writeExternal()`
and `readExternal()` methods to a class and adds `Externalizable` to the
interfaces which the class implements. The `writeExternal()` method
writes each property (or field) for the class while the `readExternal()`
method will read each one back in the same order. Properties or fields
marked as `transient` are ignored. Example usage:
[source,groovy]
-------------------------
import groovy.transform.*
@AutoExternalize
class Person {
String first, last
List favItems
Date since
}
-------------------------
Which will create a class of the following form:
[source,groovy]
-------------------------------------------------------------
class Person implements Externalizable {
...
void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(first)
out.writeObject(last)
out.writeObject(favItems)
out.writeObject(since)
}
void readExternal(ObjectInput oin) {
first = oin.readObject()
last = oin.readObject()
favItems = oin.readObject()
since = oin.readObject()
}
...
}
-------------------------------------------------------------
[[Groovy18releasenotes-Controllingtheexecutionofyourcode]]
=== Controlling the execution of your code
When integrating user-provided Groovy scripts and classes in your Java
application, you may be worried about code that would eat all your CPU
with infinite loops, or that call methods like `System.exit(0)` (for the
latter, check the section on compiler customizers, and particularly the
`SecureASTCustomizer`). It would be interesting to have a wait to
control the execution of that Groovy code, to be able to interrupt its
execution when the thread is interrupted, when a certain duration has
elapsed, or when a certain condition is met (lack of resources, etc).
Groovy 1.8 introduces three transformations for those purposes, as we
shall see in the following sections. By default, the three
transformations add some checks in at the beginning of each method body,
and each closure body, to check whether a condition of interruption is
met or not.
Note that those transformations are local (triggered by an annotation).
If you want to apply them transparently, so that the annotation doesnt
show up, I encourage you to have a look at the
`ASTTransformationCustomizer` explained at the end of this article.
Cédric Champeau, our most recent Groovy committer, who implemented those
features, has a
http://www.jroller.com/melix/entry/upcoming_groovy_goodness_automatic_thread[very
nice blog post] covering those code interruption transformations.
[[Groovy18releasenotes-ThreadInterrupt]]
==== @ThreadInterrupt
You dont need to write checks in your scripts for whether the current
thread of execution has been interrupted or not, by default, the
transformation will add those checks for you for scripts and classes, at
the beginning of each method body and closure body:
[source,groovy]
---------------------------------------
@ThreadInterrupt
import groovy.transform.ThreadInterrupt
while (true) {
// eat lots of CPU
}
---------------------------------------
You can specify a `checkOnMethodStart` annotation parameter (defaults to
true) to customize where checks are added by the transformation (adds an
interrupt check by default as the first statement of a method body). And
you can also specify the `applyToAllClasses` annotation parameter
(default to true) if you want to specify whether only the current class
or script should have this interruption logic applied or not.
[[Groovy18releasenotes-TimedInterrupt]]
==== @TimedInterrupt
With `@TimedInterrupt`, you can interrupt the script after a certain
amount of time:
[source,groovy]
--------------------------------------
@TimedInterrupt(10)
import groovy.transform.TimedInterrupt
while (true) {
// eat lots of CPU
}
--------------------------------------
In addition to the previous annotation parameters we mentioned for
`@ThreadInterrupt`, you should specify `value`, the amount of time to
wait, and `unit` (defaulting to `TimeUnit.SECONDS`) to specify the unit
of time to be used.
[[Groovy18releasenotes-ConditionalInterrupt]]
==== @ConditionalInterrupt
An example of `@ConditionalInterrupt` which leverages the closure
annotation parameter feature, and the `@Field` transformation as well:
[source,groovy]
--------------------------------------------
@ConditionalInterrupt({ counter++ > 2 })
import groovy.transform.ConditionalInterrupt
import groovy.transform.Field
@Field int counter = 0
100.times {
println 'executing script method...'
}
--------------------------------------------
You can imagine defining any kind of condition: on counters, on resource
availability, on resource usage, and more.
[[Groovy18releasenotes-ToString]]
=== @ToString
Provides your classes with a default `toString()` method which prints
out the values of the class properties (and optionally the property
names and optionally fields). A basic example is here:
[source,groovy]
-----------------------------------------
import groovy.transform.ToString
@ToString
class Person {
String name
int age
}
println new Person(name: 'Pete', age: 15)
// => Person(Pete, 15)
-----------------------------------------
And heres another example using a few more options:
[source,groovy]
----------------------------------------------------
@ToString(includeNames = true, includeFields = true)
class Coord {
int x, y
private z = 0
}
println new Coord(x:20, y:5)
// => Coord(x:20, y:5, z:0)
----------------------------------------------------
[[Groovy18releasenotes-EqualsAndHashCode]]
=== @EqualsAndHashCode
Provides your classes with `equals()` and `hashCode()` methods based on
the values of the class properties (and optionally fields and
optionally super class values for `equals()` and `hashCode()`).
[source,groovy]
-----------------------------------------
import groovy.transform.EqualsAndHashCode
@EqualsAndHashCode
class Coord {
int x, y
}
def c1 = new Coord(x:20, y:5)
def c2 = new Coord(x:20, y:5)
assert c1 == c2
assert c1.hashCode() == c2.hashCode()
-----------------------------------------
[[Groovy18releasenotes-TupleConstructor]]
=== @TupleConstructor
Provides a tuple (ordered) constructor. For POGOs (plain old Groovy
objects), this will be in addition to Groovys default `named-arg`
constructor.
[source,groovy]
---------------------------------------------------------
import groovy.transform.TupleConstructor
@TupleConstructor
class Person {
String name
int age
}
def p1 = new Person(name: 'Pete', age: 15) // map-based
def p2 = new Person('Pete', 15) // tuple-based
assert p1.name == p2.name
assert p1.age == p2.age
---------------------------------------------------------
[[Groovy18releasenotes-Canonical]]
=== @Canonical
Allows you to combine `@ToString`, `@EqualsAndHashCode` and
`@TupleConstructor`. For those familiar with Groovys `@Immutable`
transform, this provides similar features but for mutable objects.
[source,groovy]
--------------------------------------------
import groovy.transform.Canonical
@Canonical
class Person {
String name
int age
}
def p1 = new Person(name: 'Pete', age: 15)
def p2 = new Person('Paul', 15)
p2.name = 'Pete'
println "${p1.equals(p2)} $p1 $p2"
// => true Person(Pete, 15) Person(Pete, 15)
--------------------------------------------
By default, `@Canonical` gives you vanilla versions for each of the
combined annotations. If you want to use any of the special features
that the individual annotations give you, simply include the individual
annotation as well.
[source,groovy]
-----------------------------------------
import groovy.transform.*
@Canonical
@ToString(includeNames = true)
class Person {
String name
int age
}
def p = new Person(name: 'Pete', age: 15)
println p
// => Person(name:Pete, age:15)
-----------------------------------------
You will find a great
http://prystash.blogspot.com/2011/04/groovy-18-playing-with-new-canonical.html[write-up
on @Canonical, @ToString, @EqualsAndHashCode and @TupleConstructor] on
John Prystashs weblog.
[[Groovy18releasenotes-InheritConstructors]]
=== @InheritConstructors
Sometimes, when you want to subclass certain classes, you also need to
override all the constructors of the parent, even if only to call the
super constructor. Such a case happens for instance when you define your
own exceptions, you want your exceptions to also have the constructors
taking messages and throwable as parameters. But instead of writing this
kind of boilerplate code each time for your exceptions:
[source,groovy]
--------------------------------------------------------------
class CustomException extends Exception {
CustomException() { super() }
CustomException(String msg) { super(msg) }
CustomException(String msg, Throwable t) { super(msg, t) }
CustomException(Throwable t) { super(t) }
}
--------------------------------------------------------------
Simply use the @InheritConstructors transformation which takes care of
overriding the base constructors for you:
[source,groovy]
------------------------------------------
import groovy.transform.*
@InheritConstructors
class CustomException extends Exception {}
------------------------------------------
[[Groovy18releasenotes-WithReadLockandWithWriteLock]]
=== @WithReadLock and @WithWriteLock
Those two transformations, combined together, simplify the usage of
`java.util.concurrent.locks.ReentrantReadWriteLock`, are safer to use
than the `synchronized` keyword, and improve upon the `@Synchronized`
transformation with a more granular locking.
More concretely, with an example, the following:
[source,groovy]
------------------------------------------------------------
import groovy.transform.*
class ResourceProvider {
private final Map<String, String> data = new HashMap<>()
@WithReadLock
String getResource(String key) {
return data.get(key)
}
@WithWriteLock
void refresh() {
//reload the resources into memory
}
}
------------------------------------------------------------
Will generate code as follows:
[source,groovy]
-----------------------------------------------------------------------------
import java.util.concurrent.locks.ReentrantReadWriteLock
import java.util.concurrent.locks.ReadWriteLock
class ResourceProvider {
private final ReadWriteLock $reentrantlock = new ReentrantReadWriteLock()
private final Map<String, String> data = new HashMap<String, String>()
String getResource(String key) {
$reentrantlock.readLock().lock()
try {
return data.get(key)
} finally {
$reentrantlock.readLock().unlock()
}
}
void refresh() throws Exception {
$reentrantlock.writeLock().lock()
try {
//reload the resources into memory
} finally {
$reentrantlock.writeLock().unlock()
}
}
}
-----------------------------------------------------------------------------
[[Groovy18releasenotes-ListenerList]]
=== @ListenerList
If you annotate a Collection type field with @ListenerList, it generates
everything that is needed to follow the bean event pattern. This is kind
of an EventType independent version of what @Bindable is for
PropertyChangeEvents.
This example shows the most basic usage of the @ListenerList annotation.
The easiest way to use this annotation is to annotate a field of type
List and give the List a generic type. In this example we use a List of
type MyListener. MyListener is a one method interface that takes a
MyEvent as a parameter. The following code is some sample source code
showing the simplest scenario.
[source,groovy]
-----------------------------------------
interface MyListener {
void eventOccurred(MyEvent event)
}
class MyEvent {
def source
String message
MyEvent(def source, String message) {
this.source = source
this.message = message
}
}
class MyBeanClass {
@ListenerList
List<MyListener> listeners
}
-----------------------------------------
* + addMyListener(MyListener) : void - This method is created based on
the generic type of your annotated List field. The name and parameter
type is are based on the List fields generic parameter.
* + removeMyListener(MyListener) : void- This method is created based on
the generic type of your annotated List field. The name and parameter
type is are based on the List fields generic parameter.
* + getMyListeners() : MyListener[] - This method is created based on
the generic type of your annotated List field.The name is the plural
form of the List fields generic parameter, and the return type is an
array of the generic parameter.
* + fireEventOccurred(MyEvent) : void - This method is created based on
the type that the Lists generic type points to. In this case,
MyListener is a one method interface with an eventOccurred(MyEvent)
method. The method name is fire[MethodName of the interface] and the
parameter is the parameter list from the interface. A fireX method will
be generated for each public method in the target class, including
overloaded methods
[[Groovy18releasenotes-AlignmentswithJDK7]]
== Alignments with JDK 7
Groovy 1.9 will be the version which will align as much as possible with
the upcoming JDK 7, so beyond those aspects already covered in Groovy
(like strings in switch and others), most of those `Project Coin`
proposals will be in 1.9, except the `diamond operator` which was
added in 1.8, as explained in the following paragraph.
[[Groovy18releasenotes-Diamondoperator]]
=== Diamond operator
Java 7 will introduce the `diamond` operator in generics type
information, so that you can avoid the usual repetition of the
parameterized types. Groovy decided to adopt the notation before JDK 7
is actually released. So instead of writing:
[source,groovy]
--------------------------------------------------------
List<List<String>> list1 = new ArrayList<List<String>>()
--------------------------------------------------------
You can _omit_ the parameterized types and just use the pointy
brackets, which now look like a diamond:
[source,groovy]
--------------------------------------------
List<List<String>> list1 = new ArrayList<>()
--------------------------------------------
[[Groovy18releasenotes-NewDGMmethods]]
== New DGM methods
* count Closure variants
[source,groovy]
---------------------------------------------
def isEven = { it % 2 == 0 }
assert [2,4,2,1,3,5,2,4,3].count(isEven) == 5
---------------------------------------------
* countBy
[source,groovy]
----------------------------------------------------------------------
assert [0:2, 1:3] == [1,2,3,4,5].countBy{ it % 2 }
assert [(true):2, (false):4] == 'Groovy'.toList().countBy{ it == 'o' }
----------------------------------------------------------------------
* plus variants specifying a starting index
[source,groovy]
-------------------------------------------------------
assert [10, 20].plus(1, 'a', 'b') == [10, 'a', 'b', 20]
-------------------------------------------------------
* equals for Sets and Maps now do flexible numeric comparisons (on
values for Maps)
[source,groovy]
----------------------------------------
assert [1L, 2.0] as Set == [1, 2] as Set
assert [a:2, b:3] == [a:2L, b:3.0]
----------------------------------------
* toSet for primitive arrays, Strings and Collections
[source,groovy]
-----------------------------------------------------------
assert [1, 2, 2, 2, 3].toSet() == [1, 2, 3] as Set
assert 'groovy'.toSet() == ['v', 'g', 'r', 'o', 'y'] as Set
-----------------------------------------------------------
* min / max methods for maps taking closures +
(also available in Groovy 1.7)
[source,groovy]
----------------------------------------------
def map = [a: 1, bbb: 4, cc: 5, dddd: 2]
assert map.max { it.key.size() }.key == 'dddd'
assert map.min { it.value }.value == 1
----------------------------------------------
* map withDefault\{} +
Oftentimes, when using a map, for example for counting the frequency of
words in a document, you need to check that a certain key exists, before
doing something with the associating value (like incrementing it).
Nothing really complex, but we could improve upon that with a new
method, called `withDefault`. So instead of writing code like below:
[source,groovy]
---------------------------------------------------
def words = "one two two three three three".split()
def freq = [:]
words.each {
if (it in freq)
freq[it] += 1
else
freq[it] = 1
}
---------------------------------------------------
Thanks to the new method (also backported to 1.7), you can write the
example as follows:
[source,groovy]
---------------------------------------------------
def words = "one two two three three three".split()
def freq = [:].withDefault { k -> 0 }
words.each {
freq[it] += 1
}
---------------------------------------------------
[[Groovy18releasenotes-Miscellaneous]]
== Miscellaneous
[[Groovy18releasenotes-Slashystrings]]
=== Slashy strings
Slashy strings are now multi-line:
[source,groovy]
-----------------------------------
def poem = /
to be
or
not to be
/
assert poem.readLines().size() == 4
-----------------------------------
This is particularly useful for multi-line regexs when using the regex
free-spacing comment style (though you would still need to escape
slashes):
[source,groovy]
-------------------------------------------------------------------------------------------
// match yyyy-mm-dd from this or previous century
def dateRegex = /(?x) # enable whitespace and comments
((?:19|20)\d\d) # year (group 1) (non-capture alternation for century)
- # seperator
(0[1-9]|1[012]) # month (group 2)
- # seperator
(0[1-9]|[12][0-9]|3[01]) # day (group 3)
/
assert '04/04/1988' == '1988-04-04'.find(dateRegex) { all, y, m, d -> [d, m, y].join('/') }
-------------------------------------------------------------------------------------------
[[Groovy18releasenotes-Dollarslashystrings]]
=== Dollar slashy strings
A new string notation has been introduced: the `dollar slashy` string.
This is a multi-line GString similar to the slashy string, but with
slightly different escaping rules. You are no longer required to escape
slash (with a preceding backslash) but you can use `$$' to escape a `$'
or `$/' to escape a slash if needed. Heres an example of its usage:
[source,groovy]
------------------------
def name = "Guillaume"
def date = "April, 21st"
def dollarSlashy = $/
Hello $name,
today we're ${date}
$ dollar-sign
$$ dollar-sign
\ backslash
/ slash
$/ slash
/$
println dollarSlashy
------------------------
This form of string is typically used when you wish to embed content
that may naturally contains slashes or backslashes and you don’t want to
have to rework the content to include all of the necessary escaping.
Some examples are shown below:
* Embedded XML fragments with backslashes:
[source,groovy]
-------------------------------------------
def tic = 'tic'
def xml = $/
<xml>
$tic\tac
</xml>
/$
assert "\n<xml>\ntic\\tac\n</xml>\n" == xml
-------------------------------------------
Better than a normal (now multi-line) slashy string where you would have
to escape the slashes or a triple quote (``'''``) GString where you would
have to escape the backslashes.
* Or windows pathnames containing a slash at the end:
[source,groovy]
----------------------
def dir = $/C:\temp\/$
----------------------
Previously, triple quote (`'''`) GString required extra escaping, and
the above sequence was illegal for a normal slashy string. Now, ugly
workarounds are not needed.
* Embedding multi-line regexs when using the regex free-spacing comment
style (particularly ones which contain slashes):
[source,groovy]
-------------------------------------------------------------------------------------------
// match yyyy-mm-dd from current or previous century
def dateRegex = $/(?x) # enable whitespace and comments
((?:19|20)\d\d) # year (group 1) (non-capture alternation for century)
[- /.] # seperator
(0[1-9]|1[012]) # month (group 2)
[- /.] # seperator
(0[1-9]|[12][0-9]|3[01]) # day (group 3)
/$
assert '04/04/1988' == '1988-04-04'.find(dateRegex) { all, y, m, d -> [d, m, y].join('/') }
assert '10-08-1989' == '1989/08/10'.find(dateRegex) { all, y, m, d -> [d, m, y].join('-') }
-------------------------------------------------------------------------------------------
So, you can cut and paste most PERL regex examples without further
escaping.
* Or Strings which are themselves Groovy code fragments containing
slashes:
[source,groovy]
-----------------------------------
def alphabet = ('a'..'z').join('')
def code = $/
def normal = '\b\t\n\r'
def slashy = /\b\t\n\r/
assert '$alphabet'.size() == 26
assert normal.size() == 4
assert slashy.size() == 8
/$
println code
Eval.me(code)
-----------------------------------
Again allowing you to cut and paste many slashy string Groovy examples
and have them embedded within dollar slashy strings without further
escaping.
[[Groovy18releasenotes-Compilationcustomizers]]
=== Compilation customizers
The compilation of Groovy code can be configured through the
`CompilerConfiguration` class, for example for setting the encoding of
your sources, the base script class, the recompilation parameters, etc).
`CompilerConfiguration` now has a new option for setting _compilation
customizers_ (belonging to the `org.codehaus.groovy.control.customizers`
package). Those customizers allow to customize the compilation process
in three ways:
* adding default imports with the `ImportCustomizer`: so you dont have
to always add the same imports all over again
* securing your scripts and classes with the `SecureASTCustomizer`: by
allowing/disallowing certain classes, or special AST nodes (Abstract
Syntax Tree), filtering imports, you can secure your scripts to avoid
malicious code or code that would go beyond the limits of what the code
should be allowed to do.
* applying AST transformations with the `ASTTransformationCustomizer`:
lets you apply transformations to all the class nodes of your
compilation unit.
For example, if you want to apply the @Log transformation to all the
classes and scripts, you could do:
[source,groovy]
-----------------------------------------------------------------------------
import org.codehaus.groovy.control.CompilerConfiguration
import org.codehaus.groovy.control.customizers.*
import groovy.util.logging.Log
def configuration = new CompilerConfiguration()
configuration.addCompilationCustomizers(new ASTTransformationCustomizer(Log))
def shell = new GroovyShell(configuration)
shell.evaluate("""
class Car {
Car() {
log.info 'Car constructed'
}
}
log.info 'Constructing a car'
def c = new Car()
""")
-----------------------------------------------------------------------------
This will log the two messages, the one from the script, and the one
from the Car class constructor, through java.util.logging. No need to
apply the @Log transformation manually to both the script and the class:
the transformation is applied to all class nodes transparently. This
mechanism can also be used for adding global transformations, just for
the classes and scripts that you compile, instead of those global
transformations being applied to all scripts and classes globally.
If you want to add some default imports (single import, static import,
star import, star static imports, and also aliased imports and static
imports), you can use the import customizer as follows:
[source,groovy]
--------------------------------------------------------
import org.codehaus.groovy.control.CompilerConfiguration
import org.codehaus.groovy.control.customizers.*
def configuration = new CompilerConfiguration()
def custo = new ImportCustomizer()
custo.addStaticStar(Math.name)
configuration.addCompilationCustomizers(custo)
def shell = new GroovyShell(configuration)
shell.evaluate("""
cos PI/3
""")
--------------------------------------------------------
When you want to evaluate Math expressions, you dont need anymore to
use the `import static java.lang.Math.*` star static import to import
all the Math constants and static functions.
[[Groovy18releasenotes-GStringtoEnumcoercion]]
=== (G)String to Enum coercion
Given a String or a GString, you can coerce it to Enum values bearing
the same name, as the sample below presents:
[source,groovy]
-----------------------------
enum Color {
red, green, blue
}
// coercion with as
def r = "red" as Color
// implicit coercion
Color b = "blue"
// with GStrings too
def g = "${'green'}" as Color
-----------------------------
[[Groovy18releasenotes-MapssupportisCase]]
=== Maps support isCase()
Maps now support `isCase()`, so you can use maps in your switch/case
statements, for instance:
[source,groovy]
----------------------------------------------
def m = [a: 1, b: 2]
def val = 'a'
switch (val) {
case m: "key in map"; break
// equivalent to // case { val in m }: ...
default: "not in map"
}
----------------------------------------------
[[Groovy18releasenotes-GrapeGrabImprovements]]
=== Grape/Grab Improvements
[[Groovy18releasenotes-ShorternotationforGrabResolver]]
==== Shorter notation for @GrabResolver
When you need to specify a special grab resolver, for when the artifacts
you need are not stored in Maven central, you could use:
[source,groovy]
----------------------------------------------------------------------
@GrabResolver(name = 'restlet.org', root = 'http://maven.restlet.org')
@Grab('org.restlet:org.restlet:2.0.6')
import org.restlet.Restlet
----------------------------------------------------------------------
Groovy 1.8 adds a shorter syntax as well:
[source,groovy]
-----------------------------------------
@GrabResolver('http://maven.restlet.org')
@Grab('org.restlet:org.restlet:2.0.6')
import org.restlet.Restlet
-----------------------------------------
[[Groovy18releasenotes-CompactformforoptionalGrabattributes]]
==== Compact form for optional Grab attributes
The `@Grab` annotation has numerous options. For example, to download
the Apache commons-io library (where you wanted to set the `transitive`
and `force` attributes - not strictly needed for this example but see
the Grab or Ivy documentation for details on what those attributes do)
you could use a grab statement similar to below:
[source,groovy]
---------------------------------------------------------------------------------------------
@Grab(group='commons-io', module='commons-io', version='2.0.1', transitive=false, force=true)
---------------------------------------------------------------------------------------------
The compact form for grab which allows the artifact information to be
represented as a string now supports specifying additional attributes.
As an example, the following script will download the commons-io jar and
the corresponding javadoc jar before using one of the commons-io
methods.
[source,groovy]
----------------------------------------------------------------
@Grab('commons-io:commons-io:2.0.1;transitive=false;force=true')
@Grab('commons-io:commons-io:2.0.1;classifier=javadoc')
import static org.apache.commons.io.FileSystemUtils.*
assert freeSpaceKb() > 0
----------------------------------------------------------------
[[Groovy18releasenotes-Sqlimprovements]]
=== Sql improvements
The `eachRow` and `rows` methods in the `groovy.sql.Sql` class now
support paging. Heres an example:
[source,groovy]
---------------------------------------------------
sql.eachRow('select * from PROJECT', 2, 2) { row ->
println "${row.name.padRight(10)} ($row.url)"
}
---------------------------------------------------
Which will start at the second row and return a maximum of 2 rows.
Heres an example result from a database containing numerous projects
with their URLs:
----------------------------------------
Grails (http://grails.org)
Griffon (http://griffon-framework.org)
----------------------------------------
[[Groovy18releasenotes-StoringASTnodemetadata]]
=== Storing AST node metadata
When developing AST transformations, and particularly when using a
visitor to navigate the AST nodes, it is sometimes tricky to keep track
of information as you visit the tree, or if a combination of transforms
need to be sharing some context. The `ASTNode` base class features 4
methods to store node metadata:
* `public Object getNodeMetaData(Object key)`
* `public void copyNodeMetaData(ASTNode other)`
* `public void setNodeMetaData(Object key, Object value)`
* `public void removeNodeMetaData(Object key)`
[[Groovy18releasenotes-AbilitytocustomizetheGroovyDoctemplates]]
=== Ability to customize the GroovyDoc templates
GroovyDoc uses hard-coded templates to create the JavaDoc for your
Groovy classes. Three templates are used: top-level templates, a
package-level template, a class template. If you want to customize these
templates, you can subclass the `Groovydoc` Ant task and override the
`getDocTemplates()`, `getPackageTemplates()`, and `getClassTemplates()`
methods pointing at your own templates. Then you can use your custom
GroovyDoc Ant task in lieu of Groovys original one.