Preview of Groovy 3

This blog post is a translation of @daniel_sun‘s post, originally posted in Chinesse.

The next section will focus on the new parser “Parrot” features coming to Groovy 3. If you wish to be able to get yourself back and forth, follow the steps below to start Groovy 3’s groovy console to try these new features:

$ git clone https://github.com/danielsun1106/groovy-parser.git
$ cd groovy-parser
$ ./gradlew groovyConsole

New feature preview

Improve the loop statement

Prior to Groovy 3, Groovy neither supported the do-while statement nor supported statements that really conform to the Java syntax specification (such as multiple initialization and multiple update expressions). In Groovy 3, complete support for looping statements makes Java background developers smoother over Groovy development. Give some examples:

Do-while statement example

int i = 0;
do {
    i++
} while (i < 5)
assert i == 5

An example of a for statement that conforms to the Java syntax specification

def result = 0
for (int i = 0, j = 0; i < 5 && j < 5; i = i + 2, j++) {
    result += i;
    result += j;
}
assert 9 == result

2. Supports Lambda expressions

Java 8 introduces the Lambda expression, in order to better compatible with Java syntax, Groovy 3 also added support for the syntax features. Give some examples:

Lambda expression example

assert 9 == [1, 2, 3].stream().map(e -> e + 1).reduce(0, (r, e) -> r + e)

3. Support method reference (reference) and constructor reference (constructor reference)

In addition to introducing Lambda expressions, Java 8 introduces the method reference and the constructor reference, and for the same reason, Groovy 3 adds support for the syntax feature. Give some examples:

Method reference

assert ['A', 'B', 'C'] == ['a', 'b', 'c'].stream().map(String::toUpperCase).collect(Collectors.toList())

Constructor reference example

assert [1, 2, 3] as String[] == [1, 2, 3].stream().map(String::valueOf).toArray(String[]::new)

4. Support for try-with-resources statements

Java 7 introduced the automatic resource management mechanism, developers can try-with-resources statement is very easy to complete the management of resources. It is also supported in Groovy 3. Give some examples:

Try-with-resources statement example

class Resource implements Closeable {
    int resourceId;
    static closedResourceIds = [];
    public Resource(int resourceId) {
        this.resourceId = resourceId;
    }
    public void close() {
        closedResourceIds << resourceId
    }
}
def a = 1;
try (Resource r1 = new Resource(1)) {
    a = 2;
}
assert Resource.closedResourceIds == [1]
assert 2 == a

5. Support for code blocks

There is an infrequent but useful syntax in Java that can easily isolate the scope of the variable, the code block. Groovy 3 also supported it. Give some examples:

Code block example

{
    def a = 1
    a++
    assert 2 == a
}
{
    def a = 1
    assert 1 == a
}

6. Support Java-style array initialization

In order to better compatible with Java syntax, Groovy 3 added support for Java-style array initialization.

Array initialization example

def a = new int[] {1, 2}
    assert a[0] == 1
    assert a[1] == 2
    assert a as List == [1, 2]

7. support interface default method (default method)

Among the many new features introduced in Java 8, the default method of interface is also a useful feature. Groovy 3 naturally will not omit its support. Give some examples:

Default method

interface Greetable {
    String name();
    default String hello() {
        return 'hello'
    }
    default public String sayHello() {
        return this.hello() + ', ' + this.name()
    }
}
class Person implements Greetable {
    @Override
    public String name() {
        return 'Daniel'
    }
}
def p = new Person()
assert 'hello, Daniel' == "${p.hello()}, ${p.name()}"
assert 'hello, Daniel' == p.sayHello()

8. New operator: consistency operator (===,! ==), Elvis assignment (? =),! In,! Instanceof

In order to make the Groovy program more concise, Groovy 3 introduced these new operators. Give some examples:

The consistency operator (===,! ==) example
assert ‘abc’ === ‘abc’
assert ‘abc’ !== new String(‘abc’)
Elvis assignment (? =) Example

def a = 2
a ?= 1
assert a == 2
a = null
a ?= 1
assert a == 1

!in Example

assert 1 !in [2, 3, 4]

!instanceof Example

assert 1 !instanceof String

9. Support for secure retrieval

Groovy has a safe way of doing so, that is, references to. This avoids the occurrence of NullPointerException, but lacks support for secure retrieval of collections and arrays, and Groovy 3 makes up for this shortcoming. Give some examples:

Safe search example

String[] array = ['a', 'b'];
assert 'b' == array?[1];
array?[1] = 'c';
assert 'c' == array?[1];
array = null;
assert null == array?[1];
array?[1] = 'c';
assert null == array?[1];

10. Supports runtime Groovydoc and saves Groovydoc as metadata in the AST node

Groovy 3 adds runtime Groovydoc, which simply adds @Groovydoc at the beginning of the Groovydoc content to access the Groovydoc content at run time. If Javadoc is accompanied by Java source code is a reflection of the document, then the run-time Groovydoc is further, it is stored in bytecode, and the program has done a real combination.

Runtime Groovydoc example

/**
 * @Groovydoc
 * class AA
 */
class AA {
    /**
     * @Groovydoc
     * field SOME_FIELD
     */
    public static final int SOME_FIELD = 1;
    /**
     * @Groovydoc
     * constructor AA
     */
    public AA() {
    }
    /**
     * @Groovydoc
     * method m
     */
    public void m() {
    }
    /**
     * @Groovydoc
     * class InnerClass
     */
    class InnerClass {
    }
}
/**
 * @Groovydoc
 * annotation BB
 */
@interface BB {
}
assert AA.class.getAnnotation(groovy.lang.Groovydoc).value().contains('class AA')
assert AA.class.getMethod('m', new Class[0]).getAnnotation(groovy.lang.Groovydoc).value().contains('method m')
assert AA.class.getConstructor().getAnnotation(groovy.lang.Groovydoc).value().contains('constructor AA')
assert AA.class.getField('SOME_FIELD').getAnnotation(groovy.lang.Groovydoc).value().contains('field SOME_FIELD')
assert AA.class.getDeclaredClasses().find {it.simpleName.contains('InnerClass')}.getAnnotation(groovy.lang.Groovydoc).value().contains('class InnerClass')
assert BB.class.getAnnotation(groovy.lang.Groovydoc).value().contains('annotation BB')

An example of saving Groovydoc as a metadata in an AST node

import org.codehaus.groovy.control.*
import static org.apache.groovy.parser.antlr4.GroovydocManager.DOC_COMMENT
def code = '''
/**
 * Groovydoc for hello method
 * @author Daniel Sun
 */
void hello(String name) {}
'''
def ast = new CompilationUnit().tap {
    addSource 'hello.groovy', code
    compile Phases.SEMANTIC_ANALYSIS 
}.ast
assert ast.classes[0].methods.grep(e -> e.name == 'hello')[0].nodeMetaData[DOC_COMMENT].contains('Groovydoc for hello method')

One thought on “Preview of Groovy 3

Leave a Reply

Your email address will not be published. Required fields are marked *