修改本页

1. Groovy语言规范

1.1. 语法

本章将覆盖Groovy编程语言的语法。 Groovy语言的语法是由JAVA语言的语法衍生出来, 但具体结构上会有所增强,同时容许一定的简化。

1.1.1. 注释

单行注释

单行注释以//开始,同时它可以出现在行的任何地方。 在//之后的字符, 一直到行尾都是注释的部分。

// a standalone single line comment

println "hello" // a comment till the end of the line
多行注释

一个多行的注释以/*开头,同时它可以出现在行的任何地方。 在/*之后的字符将会被视作注释的一部分,包括新一行的字符, 直到第一个*/来结束注释。 多行注释因此可以被放到行尾,或者甚至在句子中间。

/* a standalone multiline comment

   spanning two lines */

println "hello" /* a multiline comment starting

                   at the end of a statement */

println 1 /* one */ + 2 /* two */
GroovyDoc注释

与多行注释类似,GroovyDoc注释是多行的,但是是以/**开始和以*/结尾。 在第一行GroovyDoc注释之后的行可以可选地以*开头。 这些注释的组成有:

  • 类型定义(类,接口,枚举,注解),

  • 字段和属性定义

  • 方法定义

虽然编译器不会抱怨GroovyDoc注释没后包含上面的语言元素,但你应该在做注释之前预先考虑前面提到的这些结构。

/**

 * A Class description

 */

class Person {

    /** the name of the person */

    String name


    /**

     * Creates a greeting method for a certain person.

     *

     * @param otherPerson the person to greet

     * @return ag reeting message

     */

    String greet(String otherPerson) {

       "Hello ${otherPerson}"

    }

}

GroovyDoc遵循与Java’s自带的JavaDoc一样的约定。 所以你可以使用于JavaDoc一样的标记。

Shebang行

除了单行注释外, 还有一种被特别的行注释, 通常被称作shebang行,它通常在UNIX系统中被认知,它容许脚本直接在命令行中运行那些你已经安装的Groovy和那些已经在PATH中可用的groovy命令。

#!/usr/bin/env groovy

println "Hello from the shebang line"
#字符必须是文件的第一个字符。 任何缩进都会引起一个编译错误。

1.1.2. 关键字

下面的列表列出了全部Groovy语言的关键字:

表1. 关键字

as

assert

break

case

catch

class

const

continue

def

default

do

else

enum

extends

false

finally

for

goto

if

implements

import

in

instanceof

interface

new

null

package

return

super

switch

this

throw

throws

true

try

while

1.1.3. 标识符

普通标识符

标识符以字母,美元符或下划线开头。不能以数字开头

字母可以是以下的范围:

  • a to z (小写 ascii 字母)

  • A to Z (大写 ascii 字母)

  • \u00C0 to \u00D6

  • \u00D8 to \u00F6

  • \u00F8 to \u00FF

  • \u0100 to \uFFFE

首字母之后的字符可以包含字母与数字。

这里有一些合法的表示符的例子(这是合法的名字):

def name

def item3

def with_underscore

def $dollarStart

但以下的是不合法的标识符:

def 3tier

def a+b

def a#b

所有的跟着点号后关键字也是合法的标识符:

foo.as

foo.assert

foo.break

foo.case

foo.catch
带引号的标识符

带引号标识符出现在点号表达式的点号之后。 例如,person.name表达式中name部分就可以引号引起来成为person."name"person.'name' 有趣的是,当某些标识符包含由Java语言规范中禁止的非法字符,引号引起来后在Groovy就会被容许。 例如,像一个破折号,空格,感叹号等字符。

def map = [:]


map."an identifier with a space and double quotes" = "ALLOWED"

map.'with-dash-signs-and-single-quotes' = "ALLOWED"


assert map."an identifier with a space and double quotes" == "ALLOWED"

assert map.'with-dash-signs-and-single-quotes' == "ALLOWED"

正如我们将会在strings一节看到的那样,Groovy提供不同的字符串文字。 所有跟着点号的字符串都是被允许的:

map.'single quote'

map."double quote"

map.'''triple single quote'''

map."""triple double quote"""

map./slashy string/

map.$/dollar slashy string/$

正如下面的例子,纯字符的字符串和Groovy的GStings(字符串插值)的区别是,计算整个标识符时,插入的字符值是被插入到最终字符串:

def firstname = "Homer"

map."Simson-${firstname}" = "Homer Simson"


assert map.'Simson-Homer' == "Homer Simson"

1.1.4. 字符串

字符以链的形式形成的文本被称作字符串。 Groovy容许你实例化java.lang.String对象,以及在其他语言中被称作插值字符串(interpolated strings)的GStrings (groovy.lang.GString)

单引号字符串

单引号字符串是一系列被单引号引起来的字符:

def firstname = "Homer"

map."Simson-${firstname}" = "Homer Simson"


assert map.'Simson-Homer' == "Homer Simson"
单引号字符串是纯的java.lang.String,同时不支持插值。
字符串连接

所有Groovy的字符串都可以用+号连接起来:

assert 'ab' == 'a' + 'b'
三引号字符串

三引号字符串是一系列被单引号引起来的字符:

'a single quoted string'
三引号字符串是纯的java.lang.String,同时不支持插值。

三引号字符串可以是多行的。 你不必将字符串分割成几块,也不必用连接符或换行符转义字符来将字符串跨行:

def aMultilineString = '''line one

line two

line three'''

如果你的代码是缩进的,比如在类里面的方法,你的字符串将会包含这些空白的缩进。 Groovy开发套件提供String#stripIndent()方法来剥离(stripping out)缩进, 同时Groovy开发套件提供String#stripMargin()方法,该方法需要一个分隔符来识别从字符串的开头开始删除的文本。

当创建如下的字符串时:

def startingAndEndingWithANewline = '''

line one

line two

line three

'''

你会发现,得到的是一个以换行符开头的字符串。可以通过反斜杠转义换行来剥离该字符:

def strippedFirstNewline = '''\

line one

line two

line three

'''


assert !strippedFirstNewline.startsWith('\n')
转义特殊字符串

你可以用反斜杠来转义单引号来避免终止字符串:

'an escaped single quote: \' needs a backslash'

同时你可以用两个反斜杠转义转义符号本身:

'an escaped escape character: \\ needs a double backslash'

一些特殊的字符串都是用反斜杠来作为转义字符:

转义序列 字符

\t

制表键

\b

后退键

\n

换行

\r

回车

\f

换页

\\

反斜杠

\'

单引号 (单引号和三单引号字符串)

\"

双引号(双引号和三双引号字符串)

Unicode转义序列

对于不存在你键盘上的字符,你可以使用Unicode转义序列:反斜杠,其次是u,然后4个十六进制数字。

例如,欧元货币符号可以表示为:

'The Euro currency symbol: \u20AC'
双引号字符串

双引号字符串是一系列被双引号引起来的字符:

"a double quoted string"
如果没有插值表示式的话,双引号字符串是纯的java.lang.String, 但如果有插值存在的话,双引号字符串就是一个groovy.lang.GString实例。
为了转义一个双引号,你可以用反斜杠符号:"A double quote: \"".
字符串插值

除了单引号和三引号字符串,任何Groovy表达式都可以插值到所有的字符串里。 插值就是当计算字符串的值时,用值替换掉字符串中的占位符。 占位符表达式是用${}围起来的,或者以$为前缀的点表达式。 当一个GString被传到一个需要字符串为参数的方法时,这个GString会调用toString()来计算自己的字符串值,这时字符串里面的占位符会计算为自己表达式值的字符串形式。

在这里,我们有一个带占位符的字符串,这个占位符引用一个本地变量:

def name = 'Guillaume' // a plain string

def greeting = "Hello ${name}"


assert greeting.toString() == 'Hello Guillaume'

然而任何的Groovy表达式都是有效的,正如我们看到的这个例子,它带有一个算术表达式:

def sum = "The sum of 2 and 3 equals ${2 + 3}"

assert sum.toString() == 'The sum of 2 and 3 equals 5'
不但表达式在${}占位符里面是合法的。语句也同样是合法的,但语句的值为null。 所以如果有多个语句在占位符里面,最后的那个语句应该返回一个有意义的值,以便替换整个占位符的值。 例如, "The sum of 1 and 2 is equal to ${def a = 1; def b = 2; a + b}"是可以工作的,但一个好的做法通常是在GString里面的占位符内的表达式应该尽量简单。

除了${}占位符外,我们也可以用单一个$前缀符号的点表达式 :

def person = [name: 'Guillaume', age: 36]

assert "$person.name is $person.age years old" == 'Guillaume is 36 years old'

不单单a.b, a.b.c等点表达式是有效的, 而且像方法这种带括号的表达式表达式,带大括号的闭包,或者算术运算符都是合法的。 给定以下这个定义为数字的变量:

def number = 3.14

下面的句子会抛出一个groovy.lang.MissingPropertyException异常,因为Groovy认为你试图访问这个数字的toString这个不存在的属性:

shouldFail(MissingPropertyException) {

    println "$number.toString()"

}
你可以将"$number.toString()"想象为被解析器解释执行为"${number.toString}()".

如果你想在GString转义$${}以便不让他们显示为插值, 你只需要用反斜杠转义美元符号:

assert '${name}' == "\${name}"
插值的特殊形式闭包表达式

到目前为止,我们已经看到,我们可以插任意值到${}占位符,但这里有一个特殊情况就是闭包表达式。 当占位符包含一个箭头,${→},这个表达式事实上是一个闭包表达式 — 你可以把它看作是一个带有美元符前缀的闭包:

def sParameterLessClosure = "1 + 2 == ${-> 3}" (1)

assert sParameterLessClosure == '1 + 2 == 3'


def sOneParamClosure = "1 + 2 == ${ w -> w << 3}" (2)

assert sOneParamClosure == '1 + 2 == 3'
1 这个闭包是一个不带参数的闭包。
2 在这里,闭包接受一个java.io.StringWriter参数,你可以用<<左移运算符添加内容。 在那一个情况下,上面两个占位符都是嵌入的闭包。

在表面上,这看起来像是一种更加详细定义插值的表达式,但比起其他普通的表达,闭包有一个有趣的优点:延时计算。

一起来考虑下以下的例子:

def number = 1 (1)

def eagerGString = "value == ${number}"

def lazyGString = "value == ${ -> number }"


assert eagerGString == "value == 1" (2)

assert lazyGString ==  "value == 1" (3)


number = 2 (4)

assert eagerGString == "value == 1" (5)

assert lazyGString ==  "value == 2" (6)
1 我们定义一个值为1number类型变量,然后我们将它插值到两个GString, eagerGString里使用表达式和lazyGString里使用闭包。
2 我们期望eagerGString的结果字符串是包含字符串1的。
3 lazyGString是相类似的方式。
4 这时我们用一个新的数字修改变量
5 使用纯插值表达式的话,值实际上是被绑定到创建GString时候的值。
6 但使用闭包表达式的话,它是每次调用的时候都被强制将从GString转化为String, 结果给包含被更新的新数字值。
一个嵌入的闭包表达式接受多于一个参数的时候,将会在运行时产生一个异常。 闭包只接受零个或一个参数。
与JAVA的互操作性

当一个方法(无论是用JAVA实现还是Groovy实现)期望一个java.lang.String作为参数, 但我们传了一个groovy.lang.GString实例的时候,Gstring的toString()方法就会自动地,透明地被调用。

String takeString(String message) {         (4)

    assert message instanceof String        (5)

    return message

}


def message = "The message is ${'hello'}"   (1)

assert message instanceof GString           (2)


def result = takeString(message)            (3)

assert result instanceof String

assert result == 'The message is hello'
1 我们创建了一个GString变量
2 我们复查它的确是一个GString的实例
3 这时我们传递一个GString给一个接受String作为参数的方法
4 takeString()方法的签名明确的说明了它的唯一的一个参数是字符串类型
5 我们也验证参数确实是一个字符串,而不是一个GString。
GString和String的hashCode

虽然插值字符串可以被用来替代纯的Java字符串, 但它们有特别的方法来区分字符串:它们的hashcode是不同的。 纯Java字符串是不可变的,而GString表示的结果字符串却是可变的,这取决与它的插值。 即使是相同的结果字符串,GString和String的hasCode也是不同的。

assert "one: ${1}".hashCode() != "one: 1".hashCode()

GString和String有着不同的hasCode值,应该避免把GString用作Map的键,尤其是当我们尝试检索一个值时,应当使用String而不是GString。

def key = "a"

def m = ["${key}": "letter ${key}"]     (1)


assert m["a"] == null                   (2)
1 map创建了一个以GString为键的键值对
2 当我们尝试用String类型键获取值的时候,我们将找不到, 因为String和GString有着不同的hashCode值
三双引号字符串

三双引号字符串表现得像双引号字符串,只是它像三单引号字符串那样,是多行的。

def name = 'Groovy'

def template = """

    Dear Mr ${name},


    You're the winner of the lottery!


    Yours sincerly,


    Dave

"""


assert template.toString().contains('Groovy')
在三双引号字符串里的,无论是双引号还是单引号都需要转义的。
斜杠字符串

除了通常的带引号的字符串,Groovy提供斜杠字符串,它使用/作为分隔符。 斜杠字符串在定义正则表达式和模式的时候非常有用,因为不必要反斜杠来转义。

斜杠字符串的例子:

def fooPattern = /.*foo.*/

assert fooPattern == '.*foo.*'

只有正斜杠需要用反斜杠转义:

def escapeSlash = /The character \/ is a forward slash/

assert escapeSlash == 'The character / is a forward slash'

斜杠字符串可以是多行的:

def multilineSlashy = /one

    two

    three/


assert multilineSlashy.contains('\n')

斜杠字符串也可以被插值(例如GString):

def color = 'blue'

def interpolatedSlashy = /a ${color} car/


assert interpolatedSlashy == 'a blue car'

有几个陷阱需要注意的。

一个空的斜杠字符串不可以用两个正斜杠来表示,因为它会被Groovy解析器理解为是一个行注释。 这就是以下的断言为什么实际上不会被编译,因为它看起像个还没结束的句子:

assert '' == //
美元符斜杠字符串

美元符斜杠字符串是以$/开始和以/$结束的多行的GString。 转义的字符是美元符号,美元符号可以转义其他美元符号,或者正斜杠。 但美元符和正斜杠不是一定要去转义, 只要转义那些好像GString占位符那样以美元符开始的字符串, 或者那些以关闭美元斜杠字符串分割符号为开始的字符串。

这里有一个例子:

def name = "Guillaume"

def date = "April, 1st"


def dollarSlashy = $/

    Hello $name,

    today we're ${date}.


    $ dollar sign

    $$ escaped dollar sign

    \ backslash

    / forward slash

    $/ escaped forward slash

    $/$ escaped dollar slashy string delimiter

/$


assert [

    'Guillaume',

    'April, 1st',

    '$ dollar sign',

    '$ escaped dollar sign',

    '\\ backslash',

    '/ forward slash',

        '$/ escaped forward slash',

        '/$ escaped dollar slashy string delimiter'


        ].each { dollarSlashy.contains(it) }
字符串汇总表

字符串名

字符串语法

插值

多行

转义字符

单引号

'…'

\

三单引号

'''…'''

\

双引号

"…"

\

三双引号

"""…"""

\

斜杠

/…/

\

美元符斜杠

$/…/$

$

字符

与Java不同,Groovy中并没有一个明确的字符文字。 然而你可以有三种不同的方法,明确定义一个Groovy字符串使它实际上就是一字符:

char c1 = 'A' (1)
assert c1 instanceof Character

def c2 = 'B' as char (2)
assert c2 instanceof Character

def c3 = (char)'C' (3)
assert c3 instanceof Character
1 在声明变量的时候,通过显示声明char类型来指定它
2 使用as操作符来强制转换
3 使用chat操作符强制转换
当一个变量持有一个字符时,更倾向于第1种选择, 当一个字符值必须传给一个方法调用的时候,更倾向于其他两种(2 and 3)选择。

1.1.5. 数字

Groovy支持不同种类整型和十进制文本,由Java的Number类型支持。

整型类型

整型类型与Java的是一样的:

  • byte

  • char

  • short

  • int

  • long

  • java.lang.BigInteger

你可以用下面的声明创建这些类型的整数:

// 基本类型

byte  b = 1

char  c = 2

short s = 3

int   i = 4

long  l = 5


// 无限精度

BigInteger bi =  6

如果你使用def关键字来定义可选的类型,那么整型数字的类型就是可变的: 它能自行适配到合适的类型,以便能持有那个数字。

对于正数:

def a = 1

assert a instanceof Integer


// Integer.MAX_VALUE

def b = 2147483647

assert b instanceof Integer


// Integer.MAX_VALUE + 1

def c = 2147483648

assert c instanceof Long


// Long.MAX_VALUE

def d = 9223372036854775807

assert d instanceof Long


// Long.MAX_VALUE + 1

def e = 9223372036854775808

assert e instanceof BigInteger

同样的对于负数:

def na = -1

assert na instanceof Integer


// Integer.MIN_VALUE

def nb = -2147483648

assert nb instanceof Integer


// Integer.MIN_VALUE - 1

def nc = -2147483649

assert nc instanceof Long


// Long.MIN_VALUE

def nd = -9223372036854775808

assert nd instanceof Long


// Long.MIN_VALUE - 1

def ne = -9223372036854775809

assert ne instanceof BigInteger
可选择的基于非10进制的表示
二进制

在Java6和之前,以及在Groovy中,数字可以用十进制,八进制和十六进制来表示, 而在Java7和Groovy2,你可以使用带有0b前缀的二进制记数法:

int xInt = 0b10101111

assert xInt == 175


short xShort = 0b11001001

assert xShort == 201 as short


byte xByte = 0b11

assert xByte == 3 as byte


long xLong = 0b101101101101

assert xLong == 2925l


BigInteger xBigInteger = 0b111100100001

assert xBigInteger == 3873g


int xNegativeInt = -0b10101111

assert xNegativeInt == -175
八进制

八进制用0开头,后面跟八进制数字的经典模式来指定的。

int xInt = 077

assert xInt == 63


short xShort = 011

assert xShort == 9 as short


byte xByte = 032

assert xByte == 26 as byte


long xLong = 0246

assert xLong == 166l


BigInteger xBigInteger = 01111

assert xBigInteger == 585g


int xNegativeInt = -077

assert xNegativeInt == -63
十六进制

十六进制用0x开头,后面跟十六进制数字的经典模式来指定的。

int xInt = 0x77

assert xInt == 119


short xShort = 0xaa

assert xShort == 170 as short


byte xByte = 0x3a

assert xByte == 58 as byte


long xLong = 0xffff

assert xLong == 65535l


BigInteger xBigInteger = 0xaaaa

assert xBigInteger == 43690g


Double xDouble = new Double('0x1.0p0')

assert xDouble == 1.0d


int xNegativeInt = -0x77

assert xNegativeInt == -119
小数

小数类型与Java的一样:

  • float

  • double

  • java.lang.BigDecimal

你可以用以下声明来创建这些小数类型数字:

// primitive types

float  f = 1.234

double d = 2.345


// infinite precision

BigDecimal bd =  3.456

小数可以使用指数,使用e or E的指数表示符,后跟一个可选的符号,和一个整数表示指数值:

assert 1e3  ==  1_000.0

assert 2E4  == 20_000.0

assert 3e+1 ==     30.0

assert 4E-2 ==      0.04

assert 5e-1 ==      0.5

为了方便地进行精确的小数的计算,Groovy选择用java.lang.BigDecimal为小数的类型。 此外,无论是floatdouble都是被支持的, 但需要一个显示的类型声明,类型强制或后缀。 即使BigDecimal的是默认的小数类型,但这样的小数是可被用在以floatdouble作为参数的方法或闭包中的。

小数不能使用二进制,八进制或十六进制表示形式来表示。
在数字中的下划线

当在输入一个很长的数字是,是很难看清楚这些数字的, 通过容许在数字之间输入下划线来分组,这样可以更容易分清楚这些数字:

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
Number类型的后缀

我们可以用指定后缀(看下表)的方式强制指定一个数字的类型(包括二进制,八进制和十六进制),而这些后缀是不区分大小写的。

类型 后缀

BigInteger

G or g

Long

L or l

Integer

I or i

BigDecimal

G or g

Double

D or d

Float

F or f

例子:

assert 42I == new Integer('42')

assert 42i == new Integer('42') // lowercase i more readable

assert 123L == new Long("123") // uppercase L more readable

assert 2147483648 == new Long('2147483648') // Long type used, value too large for an Integer

assert 456G == new BigInteger('456')

assert 456g == new BigInteger('456')

assert 123.45 == new BigDecimal('123.45') // default BigDecimal type used

assert 1.200065D == new Double('1.200065')

assert 1.234F == new Float('1.234')

assert 1.23E23D == new Double('1.23E23')

assert 0b1111L.class == Long // binary

assert 0xFFi.class == Integer // hexadecimal

assert 034G.class == BigInteger // octal
算术运算

虽然操作符将会在后面详细介绍,但讨论算术运算符的行为和运算结果的类型也是非常重要的。

除了除法和指数这两个二元操作符外(后面将会介绍),

  • byte, char, shortint之间的二元运算结果将会是int

  • 涉及到longbyte, char, shortint 的结果将会是 long

  • 涉及到 BigInteger 和其他整型类型的结果将会是BigInteger

  • float, doubleBigDecimal 之间二元操作的结果将会是 double

  • 两个BigDecimal之间的二元操作结果将会是BigDecimal

下面的表格总结了这些规则:

byte char short int long BigInteger float double BigDecimal

byte

int

int

int

int

long

BigInteger

double

double

double

char

int

int

int

long

BigInteger

double

double

double

short

int

int

long

BigInteger

double

double

double

int

int

long

BigInteger

double

double

double

long

long

BigInteger

double

double

double

BigInteger

BigInteger

double

double

double

float

double

double

double

double

double

double

BigDecimal

BigDecimal

得益于Groovy的操作符重载,常用的算术运算符一样可以用于BigIntegerBigDecimal之间,而不用好像Java那样你必须使用显式方法来操作这些数字。
除法运算符的情况

如果其中一个操作数是floatdouble的话,除法运算/ (和 /= 执行除法和指定)的结果将会是double,其他的都会是BigDecimal (当操作数是short, char, byte, int, long,BigIntegerBigDecimal之间的任意组合)。

BigDecimal division is performed with the divide() method if the division is exact (ie. yielding a result that can be represented within the bounds of the same precision and scale), or using a MathContext with a precision of the maximum of the two operands’ precision plus an extra precision of 10, and a scale of the maximum of 10 and the maximum of the operands’ scale.

对于像Java中的整数除法,你应该使用intdiv()方法,而Groovy中并没有提供专用的整数除法运算符。
幂运算的情况下

幂运算是用**操作符表示,该操作符带有两个参数:底数和指数。 幂运算的结果过取决与他的操作数和运算的结果(尤其是如果结果可被表示为一个整数)

以下是Groovy的幂运算的规则,以确定结果类型:

  • 如果指数是一个十进制值

    • 如果结果可被表示为一个Integer,则返回一个Integer

    • 否则,如果结果可以表示为一个Long,然后返回一个Long

    • 否则返回一个Double

  • 如果指数为整数

    • 当指数为负,则返回一个IntegerLongDouble,这取决与结果值是否合适那个类型

    • 如果该指数是正数或零

      • 如果底数是一个BigDecimal,则返回一个BigDecimal值的结果

      • 如果底数是一个BigInteger,然后返回一个BigInteger结果值

      • 如果底数是一个整数,而且结果可以放进一个Integer类型的话,就返回Integer,否则返回一个BigInteger

      • 如果底数是一个Long,而且结果可以放进一个Long类型的话,就返回Long,否则返回一个BigInteger

我们可以用一些举例来说明这些规则:

// base and exponent are ints and the result can be represented by an Integer

assert    2    **   3    instanceof Integer    //  8

assert   10    **   9    instanceof Integer    //  1_000_000_000


// the base is a long, so fit the result in a Long

// (although it could have fit in an Integer)

assert    5L   **   2    instanceof Long       //  25


// the result can't be represented as an Integer or Long, so return a BigInteger

assert  100    **  10    instanceof BigInteger //  10e20

assert 1234    ** 123    instanceof BigInteger //  170515806212727042875...


// the base is a BigDecimal and the exponent a negative int

// but the result can be represented as an Integer

assert    0.5  **  -2    instanceof Integer    //  4


// the base is an int, and the exponent a negative float

// but again, the result can be represented as an Integer

assert    1    **  -0.3f instanceof Integer    //  1


// the base is an int, and the exponent a negative int

// but the result will be calculated as a Double

// (both base and exponent are actually converted to doubles)

assert   10    **  -1    instanceof Double     //  0.1


// the base is a BigDecimal, and the exponent is an int, so return a BigDecimal

assert    1.2  **  10    instanceof BigDecimal //  6.1917364224


// the base is a float or double, and the exponent is an int

// but the result can only be represented as a Double value

assert    3.4f **   5    instanceof Double     //  454.35430372146965

assert    5.6d **   2    instanceof Double     //  31.359999999999996


// the exponent is a decimal value

// and the result can only be represented as a Double value

assert    7.8  **   1.9  instanceof Double     //  49.542708423868476

assert    2    **   0.1f instanceof Double     //  1.0717734636432956

1.1.6. Booleans

布尔值是用来表示真值的特殊数据类型:truefalse。 可使用这种数据类型,简单地跟踪真/假的条件(conditions)

布尔值就像任何其他数据类型一样,可以存储在变量中,可以被指派到字段,:

def myBooleanVariable = true

boolean untypedBooleanVar = false

booleanField = true

truefalse是仅有的两个布尔值基元。 但更复杂的布尔表达式可以使用逻辑运算符来表示。

此外,Groovy中具有强制非布尔对象转化为布尔值的特殊规则(通常被称为Groovy Truth)。

1.1.7. 列表

Groovy使用逗号分割列表的值,使用方括号括起,来表示列表。 Groovy列表是普通的JDKjava.util.List,因为Groovy中没有定义自己的集合类。 在默认情况下,定义列表的实现具体类是java.util.ArrayList,除非你决定自己指定,正如我们将在后面看到的。

def numbers = [1, 2, 3]         (1)


assert numbers instanceof List  (2)

assert numbers.size() == 3      (3)
1 我们用逗号分割和方括号括起来的方法定义一个数字列表,同时我们指派这个列表到一个变量
2 这个列表是Java的java.util.List接口的一个实例
3 列表的长度可以用size()方法来查询,它显示列表包含3个元素

在上面的例子,我们使用了一个同质列表,但你也可以创建一个包含异构类型的值列表:

def heterogeneous = [1, "a", true]  (1)
1 我们的列表包含了一个数字,一个字符串和一个布尔值

我们提到,在默认情况下,列表实际上是java.util.ArrayList的实例, 但我们的列表使用不同的类型, 这得益于我们可以使用as操作符号强制类型转换,或者显式类型声明的变量:

def arrayList = [1, 2, 3]

assert arrayList instanceof java.util.ArrayList


def linkedList = [2, 3, 4] as LinkedList    (1)

assert linkedList instanceof java.util.LinkedList


LinkedList otherLinked = [3, 4, 5]          (2)

assert otherLinked instanceof java.util.LinkedList
1 我们使用as操作符强制明确要求一个java.util.LinkedList的实现
2 我们可以声明一个变量持有的列表是java.util.LinkedList类型的。

你可以用[]下标操作符访问列表的元素(读写值均可), 使用正数从头访问,或负数从列表的尾部访问元素,也可以使用范围, 也可以用<<左移操作符号来添加一个元素到列表:

def letters = ['a', 'b', 'c', 'd']


assert letters[0] == 'a'     (1)

assert letters[1] == 'b'


assert letters[-1] == 'd'    (2)

assert letters[-2] == 'c'


letters[2] = 'C'             (3)

assert letters[2] == 'C'


letters << 'e'               (4)

assert letters[ 4] == 'e'

assert letters[-1] == 'e'


assert letters[1, 3] == ['b', 'd']         (5)

assert letters[2..4] == ['C', 'd', 'e']    (6)
1 访问列表的第一个元素(由0来是算起)
2 用一个负数索引来访问最尾的元素:-1是从尾部算起的第一个元素
3 使用等号来设置一个新的值到列表的第三个元素
4 使用<<左移动操作符添加一个元素到列表的最后
5 一次访问两个元素,返回一个包含这两个元素的新列表
6 使用一个从开始到结束元素的位置的范围来访问列表的一个范围内的值。

如列表可以是异构性质的,列表还可以包含其他的列表来创建多维表:

def multi = [[0, 1], [2, 3]]     (1)

assert multi[1][0] == 2          (2)
1 定义一个以列表作为元素的列表
2 访问最上面列表中的第二个元素同时是内部列表中的第一个元素的元素

1.1.8. 数组

Groovy中重用列表符号表示数组,但创建这样的文字数组,你需要通过强制或类型声明,明确地定义数组类型。

String[] arrStr = ['Ananas', 'Banana', 'Kiwi']  (1)


assert arrStr instanceof String[]    (2)

assert !(arrStr instanceof List)     (3)


def numArr = [1, 2, 3] as int[]      (4)


assert numArr instanceof int[]       (5)

assert numArr.size() == 3
1 使用明确的变量类型声明定义一个字符串数组
2 断言我们建立了一个字符串数字
3 使用as操作符创建一个int数组
4 断言我们创建了原始类型的int数组

您还可以创建多维数组:

def matrix3 = new Integer[3][3]         (1)

assert matrix3.size() == 3


Integer[][] matrix2                     (2)

matrix2 = [[1, 2], [3, 4]]

assert matrix2 instanceof Integer[][]
1 你可以定义一个新的数组的边界
2 或声明数组时没有指定界限

访问数组中的元素遵循与操作列表使用相同的符号:

String[] names = ['Cédric', 'Guillaume', 'Jochen', 'Paul']

assert names[0] == 'Cédric'     (1)


names[2] = 'Blackdrag'          (2)

assert names[2] == 'Blackdrag'
1 检索数组的第一元素
2 设置一个新值到数组的第三个元素
Groovy不支持Java的数组初始符号,而大括号可能被误解为Groovy闭包的符号。

1.1.9. 映射(Maps)

在其他语言中有时也被称为字典或关联数组的特性在Groovy称为映射(maps)。 映射(maps)关联键到值,用冒号来分隔键到值,用逗号分割每对键值对,同时用方括号把整个键值包起来。

def colors = [red: '#FF0000', green: '#00FF00', blue: '#0000FF']   (1)


assert colors['red'] == '#FF0000'    (2)

assert colors.green  == '#00FF00'    (3)


colors['pink'] = '#FF00FF'           (4)

colors.yellow  = '#FFFF00'           (5)


assert colors.pink == '#FF00FF'

assert colors['yellow'] == '#FFFF00'


assert colors instanceof java.util.LinkedHashMap
1 我们定义了一个颜色名字和他们十六进制编码的html颜色相关联的映射
2 我们使用下标符号来检索与red键关联的内容
3 我们也可以使用属性表示法来断言绿色的十六进制表示
4 相似地,我们可以使用下标符号添加新的键/值对
5 或者属性访问符来添加yellow颜色
当使用该键的名字时,我们实质上是定义在映射中定义字符串键。
Groovy建立的maps实际上是java.util.LinkedHashMap的实例。 Groovy creates maps that are actually instances of java.util.LinkedHashMap.

如果你尝试用一个不存在的健来访问map:

assert colors.unknown == null

你将会获得一个null结果。

在上面的例子,我们使用的是字符串的键,但你也可以使用其他类型的值来作为键:

def numbers = [1: 'one', 2: 'two']


assert numbers[1] == 'one'

这里我们使用数字作为键,因为数字可以清楚地被识别为数字, 所以Groovy中不会产生像在我们前面的例子中一个字符串键。 但考虑下当你想传递一个变量代替键的情况,你希望这个变量的值能成为键:

def key = 'name'

def person = [key: 'Guillaume']      (1)


assert !person.containsKey('name')   (2)

assert person.containsKey('key')     (3)
1 The key associated with the 'Guillaume’ name will actually be the "key" string, not the value associated with the key variable
2 The map doesn’t contain the 'name’ key
3 Instead, the map contains a 'key’ key
You can also pass quoted strings as well as keys: ["name": "Guillaume"]. This is mandatory if your key string isn’t a valid identifier, for example if you wanted to create a string key containing a hash like in: ["street-name": "Main street"].

When you need to pass variable values as keys in your map definitions, you must surround the variable or expression with parentheses:

person = [(key): 'Guillaume']        (1)


assert person.containsKey('name')    (2)

assert !person.containsKey('key')    (3)
1 This time, we surround the key variable with parentheses, to instruct the parser we are passing a variable rather than defining a string key
2 The map does contain the name key
3 But the map doesn’t contain the key key as before

1.2. 运算符

本章涵盖了Groovy的所有运算符.

1.2.1. 算术运算符

Groovy支持在数学和其他编程语言如Java中常见的算术运算符.Java中所有的算术运算符都能够被支持.让我们通过随后的例子来认识他们.

常见算术运算符

下列的二进制算术运算符在Groovy中是有效的:

运算符 作用 备注

+

-

*

/

intdiv()用于整数除法,在这里the integer division可以看到更多关于除法的返回类型的信息.

%

**

在这里the power operation可以看到更多关于运算符返回类型的信息.

这里有几个使用这些运算符的示例:

assert  1  + 2 == 3

assert  4  - 3 == 1

assert  3  * 5 == 15

assert  3  / 2 == 1.5

assert 10  % 3 == 1

assert  2 ** 3 == 8
一元运算符

+-也可以用作一元运算符:

assert +3 == 3

assert -4 == 0 - 4


assert -(-1) == 1  (1)
1 Note the usage of parentheses to surround an expression to apply the unary minus to that surrounded expression.

依据一元算术表达式,++(叠加)和--(叠减)是有效的,无论是前缀符号还是后缀符号:

def a = 2

def b = a++ * 3             (1)


assert a == 3 && b == 6


def c = 3

def d = c-- * 2             (2)


assert c == 2 && d == 6


def e = 1

def f = ++e + 3             (3)


assert e == 2 && f == 5


def g = 4

def h = --g + 1             (4)


assert g == 3 && h == 4
1 后缀++将会在a执行和分配完值到b中之后自增1.
2 后缀--将会在c执行和分配完值到d中之后自减1.
3 前缀++将会在e执行和分配值到f中之前自加1.
4 前缀--将会在g执行和分配值到h中之前自减1.
赋值运算符

如上所见的二进制算术运算符,如下一些写法同样是有效的:

  • +=

  • -=

  • *=

  • /=

  • %=

实际使用示例:

def a = 4

a += 3


assert a == 7


def b = 5

b -= 3


assert b == 2


def c = 5

c *= 3


assert c == 15


def d = 10

d /= 2


assert d == 5


def e = 10

e %= 3


assert e == 1

1.2.2. 关系运算符

关系运算符允许对象之间比较,从而知道两个对象是相同还是不同,一个是大于还是小于或等于另一个.

下列运算符是有效的:

运算符 作用

==

相等

!=

不等

<

小于

<=

小于或等于

>

大于

>=

大于或等于

如下是这些运算符在简单数字上的使用:

assert 1 + 2 == 3

assert 3 != 4


assert -2 < 3

assert 2 <= 2

assert 3 <= 4


assert 5 > 1

assert 5 >= -2

1.2.3. 逻辑运算符

Groovy提供三个boolean值的逻辑运算符:

  • &&: 逻辑 "并"

  • ||: 逻辑 "或"

  • !: 逻辑 "非"

让我们举例说明他们的用法:

assert !false           (1)

assert true && true     (2)

assert true || false    (3)
1 "非"false即为true
2 true "并" true 仍为 true
3 true "或" false 为 true
优先级

逻辑"非"比逻辑"并"的优先级更高.

assert !false && true    (1)
1 这里的结果为true,因为"非"比"并"拥有更高的优先级,否则,这个断言将会失败.

逻辑"并"比逻辑"或"拥有更高的优先级.

assert false || true && true    (1)
1 这里的结果为true,因为"并"比"或"拥有更高的优先级,否则,这个断言将会失败.
短路的(||)

逻辑运算符"或"支持短路:如果左边的操作数为true,那么就不会再验证右边的操作数。只有当左边的操作数为false时右边的操作数才会被验证.

called = false


boolean somethingTrueOrFalse(boolean b) {  (1)

    called = true

    return b

}


assert true || somethingTrueOrFalse(false)

assert !called                              (2)


assert false || somethingTrueOrFalse(true)

assert called                               (3)
1 我们创建一个方法返回boolean类型参数,方法内部设置了called的值.
2 在第一个案例中,刚刚的方法没有被调用,因为||的左边发生了短路.
3 在第二个案例中,方法被调用了,事实证明flag现在的值为true.

1.2.4. Bitwise operators

Groovy offers 4 bitwise operators:

  • &: bitwise "and"

  • |: bitwise "or"

  • ^: bitwise "xor" (exclusive "or")

  • ~: bitwise negation

Bitwise operators can be applied on a byte or an int and return an int:

int a = 0b00101010

assert a==42

int b = 0b00001000

assert b==8

assert (a & a) == a                     (1)

assert (a & b) == b                     (2)

assert (a | a) == a                     (3)

assert (a | b) == a                     (4)


int mask = 0b11111111                   (5)

assert ((a ^ a) & mask) == 0b00000000   (6)

assert ((a ^ b) & mask) == 0b00100010   (7)

assert ((~a) & mask)    == 0b11010101   (8)
1 bitwise and
2 bitwise and returns common bits
3 bitwise or
4 bitwise or returns all 1 bits
5 setting a mask to check only the last 8 bits
6 bitwise exclusive or on self returns 0
7 bitwise exclusive or
8 bitwise negation

It’s worth noting that the internal representation of primitive types follow the Java Language Specification. In particular, primitive types are signed, meaning that for a bitwise negation, it is always good to use a mask to retrieve only the necessary bits.

In Groovy, bitwise operators have the particularity of being overloadable, meaning that you can define the behavior of those operators for any kind of object.

1.2.5. Conditional operators

Not operator

The "not" operator is represented with an exclamation mark (!) and inverts the result of the underlying boolean expression. In particular, it is possible to combine the not operator with the Groovy truth:

assert (!true)    == false                      (1)

assert (!'foo')   == false                      (2)

assert (!'')      == true                       (3)
1 the negation of true is false
2 foo is a non empty string, evaluating to true, so negation returns false
3 '' is an empty string, evaluating to false, so negation returns true
Ternary operator

The ternary operator is a shortcut expression that is equivalent to an if/else branch assigning some value to a variable.

Instead of:

if (string!=null && string.length()>0) {

    result = 'Found'

} else {

    result = 'Not found'

}

You can write:

result = (string!=null && string.length()>0)?'Found':'Not found'

The ternary operator is also compatible with the Groovy truth, so you can make it even simpler:

result = string?'Found':'Not found'
Elvis operator

The "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 or null. A simple example might look like this:

displayName = user.name ? user.name : 'Anonymous'   (1)

displayName = user.name ?: 'Anonymous'              (2)
1 with the ternary operator, you have to repeat the value you want to assign
2 with the Elvis operator, the value which is tested is used if it is not false or null

Usage of the Elvis operator reduces the verbosity of your code and reduces the risks of errors in case of refactorings, by removing the need to duplicate the expression which is tested in both the condition and the positive return value.

1.2.6. Object operators

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 }           (1)

def name = person?.name                             (2)

assert name == null                                 (3)
1 find will return a null instance
2 use of the null-safe operator prevents from a NullPointerException
3 result is null
Direct field access operator

Normally in Groovy, when you write code like this:

class User {

    public final String name                               (1)

    User(String name) { this.name = name}

    String getName() { "Name: $name" }                     (2)

}

def user = new User('Bob')

assert user.name == 'Name: Bob'                            (3)
1 public field name
2 a getter for name that returns a custom string
3 calls the getter

The user.name call triggers a call to the property of the same name, that is to say, here, to the getter for name. If you want to retrieve the field instead of calling the getter, you can use the direct field access operator:

assert user.@name == 'Bob'                                 (1)
1 use of .@ forces usage of the field instead of the getter
Method reference operator

The method reference operator (.&) call be used to store a reference to a method in a variable, in order to call it later:

def str = 'example of method reference'            (1)

def fun = str.&toUpperCase                         (2)

def upper = fun()                                  (3)

assert upper == str.toUpperCase()                  (4)
1 the str variable contains a String
2 we store a reference to the toUpperCase method on the str instance inside a variable named fun
3 fun can be called like a regular method
4 we can check that the result is the same as if we had called it directly on str

There are multiple advantages in using method references. First of all, the type of such a method reference is a groovy.lang.Closure, so it can be used in any place a closure would be used. In particular, it is suitable to convert an existing method for the needs of the strategy pattern:

def transform(List elements, Closure action) {                                      (1)

    def result = []

    elements.each {

        result << action(it)

    }

    result

}

String describe(Person p) {                                                         (2)

    "$p.name is $p.age"

}

def action = this.&describe                                                         (3)

def list = [new Person(name:'Bob', age:42), new Person(name:'Julia',age:35)]        (4)

assert transform(list, action) == ['Bob is 42', 'Julia is 35']                      (5)
1 the transform method takes each element of the list and calls the action closure on them, returning a new list
2 we define a function that takes a Person a returns a String
3 we create a method reference on that function
4 we create the list of elements we want to collect the descriptors
5 the method reference can be used where a Closure was expected

Method references are bound by the receiver and a method name. Arguments are resolved at runtime, meaning that if you have multiple methods with the same name, the syntax is not different, only resolution of the appropriate method to be called will be done at runtime:

def doSomething(String str) { str.toUpperCase() }                                   (1)

def doSomething(Integer x) { 2*x }                                                  (2)

def reference = this.&doSomething                                                   (3)

assert reference('foo') == 'FOO'                                                    (4)

assert reference(123)   == 246                                                      (5)
1 define an overloaded doSomething method accepting a String as an argument
2 define an overloaded doSomething method accepting an Integer as an argument
3 create a single method reference on doSomething, without specifying argument types
4 using the method reference with a String calls the String version of doSomething
5 using the method reference with an Integer calls the Integer version of doSomething

1.2.7. Regular expression operators

Pattern operator

The pattern operator (~) provides a simple way to create a java.util.regex.Pattern instance:

def p = ~/foo/

assert p instanceof Pattern

while in general, you find the pattern operator with an expression in a slashy-string, it can be used with any kind of String in Groovy:

p = ~'foo'                                                                              (1)

p = ~'foo'                                                                              (2)

p = ~$/dollar/slashy $ string/$                                                         (3)

p = ~"${pattern}"                                                                       (4)
1 using single quote strings
2 using double quotes strings
3 the dollar-slashy string lets you use slashes and the dollar sign without having to escape them
4 you can also use a GString!
Find operator

Alternatively to building a pattern, you can directly use the find operator =~ to build a java.util.regex.Matcher instance:

def text = "some text to match"

def m = text =~ /match/                                                                 (1)

assert m instanceof Matcher                                                             (2)

if (!m) {                                                                               (3)

    throw new RuntimeException("Oops, text not found!")

}
1 ~= creates a matcher against the text variable, using the pattern on the right hand side
2 the return type of ~= is a Matcher
3 equivalent to calling if (!m.find())

Since a Matcher coerces to a boolean by calling its find method, the =~ operator is consistent with the simple use of Perl’s =~ operator, when it appears as a predicate (in if, while, etc.).

Match operator

The match operator (==~) is a slight variation of the find operator, that does not return a Matcher but a boolean and requires a strict match of the input string:

m = text ==~ /match/                                                                    (1)

assert m instanceof Boolean                                                             (2)

if (m) {                                                                                (3)

    throw new RuntimeException("Should not reach that point!")

}
1 ~== matches the subject with the regular expression, but match must be strict
2 the return type of ~== is therefore a boolean
3 equivalent to calling if (text ==~ /match/)

1.2.8. Other operators (WIP)

Spread operator

The Spread Operator (*.) is used to invoke an action on all items of an aggregate object. It is equivalent to calling the action on each item and collecting the result into a list:

class Car {

    String make

    String model

}

def cars = [

       new Car(make:'Peugeot', model:'508'),

       new Car(make:'Renault', model:'Clio')]                                    (1)

def makes = cars*.make                                                           (2)

assert makes == ['Peugeot', 'Renault']                                           (3)
1 build a list of Car items. The list is an aggregate of objects.
2 call the spread operator on the list, accessing the make property of each item
3 returns a list of strings corresponding to the collection of make items

The spread operator is null-safe, meaning that if an element of the collection is null, it will return null instead of throwing a NullPointerException:

cars = [

   new Car(make:'Peugeot', model:'508'),

   null,                                                                         (1)

   new Car(make:'Renault', model:'Clio')]

assert cars*.make == ['Peugeot', null, 'Renault']                                (2)

assert null*.make == null                                                        (3)
1 build a list for which of of the elements is null
2 using the spread operator will not throw a NullPointerException
3 the receiver might also be null, in which case the return value is null

The spread operator can be used on any class which implements the Iterable interface:

class Component {

    Long id

    String name

}

class CompositeObject implements Iterable<Component> {

    def components = [

        new Component(id:1, name: 'Foo'),

        new Component(id:2, name:'Bar')]


    @Override

    Iterator<Component> iterator() {

        components.iterator()

    }

}

def composite = new CompositeObject()

assert composite*.id == [1,2]

assert composite*.name == ['Foo','Bar']
Spreading method arguments

There may be situations when the arguments of a method call can be found in a list that you need to adapt to the method arguments. In such situations, you can use the spread operator to call the method. For example, imagine you have the following method signature:

int function(int x, int y, int z) {

    x*y+z

}

then if you have the following list:

def args = [4,5,6]

you can call the method without having to define intermediate variables:

assert function(*args) == 26

It is even possible to mix normal arguments with spread ones:

args = [4]

assert function(*args,5,6) == 26
Spread list elements

When used inside a list literal, the spread operator acts as if the spread element contents were inlined into the list:

def items = [4,5]                      (1)

def list = [1,2,3,*items,6]            (2)

assert list == [1,2,3,4,5,6]           (3)
1 items is a list
2 we want to insert the contents of the items list directly into list without having to call addAll
3 the contents of items has been inlined into list
Spread map elements

The spread map operator works in a similar manner as the spread list operator, but for maps. It allows you to inline the contents of a map into another map literal, like in the following example:

def m1 = [c: 3, d: 4]                 (1)

def map = [a:1, b:2, *:m1]            (2)

assert map == [a:1, b:2, c:3, d:4]    (3)
1 m1 is the map that we want to inline
2 we use the *:m1 notation to spread the contents of m1 into map
3 map contains all the elements of m1

The position of the spread map operator is relevant, like illustrated in the following example:

def m1 = [c: 3, d: 4]                 (1)

def map = [a:1, b:2, *:m1, d: 8]      (2)

assert map == [a:1, b:2, c:3, d:8]    (3)
1 m1 is the map that we want to inline
2 we use the *:m1 notation to spread the contents of m1 into map, but redefine the key d after spreading
3 map contains all the expected keys, but d was redefined
Range operator

Groovy supports the concept of ranges and provides a notation (..) to create ranges of objects:

def range = 0..5                                    (1)

assert (0..5).collect() == [0, 1, 2, 3, 4, 5]       (2)

assert (0..<5).collect() == [0, 1, 2, 3, 4]         (3)

assert (0..5) instanceof List                       (4)

assert (0..5).size() == 6                           (5)
1 a simple range of integers, stored into a local variable
2 an IntRange, with inclusive bounds
3 an IntRange, with exclusive upper bound
4 a groovy.lang.Range implements the List interface
5 meaning that you can call the size method on it

Ranges implementation is lightweight, meaning that only the lower and upper bounds are stored. You can create a range from any Comparable object. For example, you can create a range of characters this way:

assert ('a'..'d').collect() == ['a','b','c','d']
Spaceship operator

The spaceship operator (<=>) delegates to the compareTo method:

assert (1 <=> 1) == 0

assert (1 <=> 2) == -1

assert (2 <=> 1) == 1

assert ('a' <=> 'z') == -1
Subscript operator

The subscript operator is a short hand notation for getAt or putAt, depending on whether you find it on the left hand side or the right hand side of an assignment:

def list = [0,1,2,3,4]

assert list[2] == 2                         (1)

list[2] = 4                                 (2)

assert list[0..2] == [0,1,4]                (3)

list[0..2] = [6,6,6]                        (4)

assert list == [6,6,6,3,4]                  (5)
1 [2] can be used instead of getAt(2)
2 if on left hand side of an assignment, will call putAt
3 getAt also supports ranges
4 so does putAt
5 the list is mutated

The subscript operator, in combination with a custom implementation of getAt/putAt is a convenient way for destructuring objects:

class User {

    Long id

    String name

    def getAt(int i) {                                                  (1)

        switch (i) {

            case 0: return id

            case 1: return name

        }

        throw new IllegalArgumentException("No such element $i")

    }

    void putAt(int i, def value) {                                      (2)

        switch (i) {

            case 0: id = value; return

            case 1: name = value; return

        }

        throw new IllegalArgumentException("No such element $i")

    }

}

def user = new User(id: 1, name: 'Alex')                                (3)

assert user[0] == 1                                                     (4)

assert user[1] == 'Alex'                                                (5)

user[1] = 'Bob'                                                         (6)

assert user.name == 'Bob'                                               (7)
1 the User class defines a custom getAt implementation
2 the User class defines a custom putAt implementation
3 create a sample user
4 using the subscript operator with index 0 allows retrieving the user id
5 using the subscript operator with index 1 allows retrieving the user name
6 we can use the subscript operator to write to a property thanks to the delegation to putAt
7 and check that it’s really the property name which was changed
Membership operator

The membership operator (in) is equivalent to calling the isCase method. In the context of a List, it is equivalent to calling contains, like in the following example:

def list = ['Grace','Rob','Emmy']

assert ('Emmy' in list)                     (1)
1 equivalent to calling list.contains('Emmy') or list.isCase('Emmy')
Identity operator

In Groovy, using == to test equality is different from using the same operator in Java. In Groovy, it is calling equals. If you want to compare reference equality, you should use is like in the following example:

def list1 = ['Groovy 1.8','Groovy 2.0','Groovy 2.3']        (1)

def list2 = ['Groovy 1.8','Groovy 2.0','Groovy 2.3']        (2)

assert list1 == list2                                       (3)

assert !list1.is(list2)                                     (4)
1 Create a list of strings
2 Create another list of strings containing the same elements
3 using ==, we test object equality
4 but using is, we can check that references are distinct
Coercion operator

The coercion operator (as) is a variant of casting. Coercion converts object from one type to another without them being compatible for assignement. Let’s take an example:

Integer x = 123

String s = (String) x                                   (1)
1 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                                  (1)
1 Integer is not assignable to a String, but use of as will coerce it to a String

When an object is coerced into another, unless the target type is the same as the source type, coercion will return a new object. The rules of coercion differ depending on the source and target types, and coercion may fail if no conversion rules are found. Custom conversion rules may be implemented thanks to the asType method:

class Identifiable {

    String name

}

class User {

    Long id

    String name

    def asType(Class target) {                                              (1)

        if (target==Identifiable) {

            return new Identifiable(name: name)

        }

        throw new ClassCastException("User cannot be coerced into $target")

    }

}

def u = new User(name: 'Xavier')                                            (2)

def p = u as Identifiable                                                   (3)

assert p instanceof Identifiable                                            (4)

assert !(p instanceof User)                                                 (5)
1 the User class defines a custom conversion rule from User to Identifiable
2 we create an instance of User
3 we coerce the User instance into an Identifiable
4 the target is an instance of Identifiable
5 the target is not an instance of User anymore
Diamond operator

The diamond operator (<>) is a syntactic sugar only operator added to support compatibility with the operator of the same name in Java 7. It is used to indicate that generic types should be inferred from the declaration:

List<String> strings = new LinkedList<>()

In dynamic Groovy, this is totally unused. In statically type checked Groovy, it is also optional since the Groovy type checker performs type inference whether this operator is present or not.

Call operator

The call operator () is used to call a method named call implicitly. For any object which defines a call method, you can omit the .call part and use the call operator instead:

class MyCallable {

    int call(int x) {           (1)

        2*x

    }

}


def mc = new MyCallable()

assert mc.call(2) == 4          (2)

assert mc(2) == 4               (3)
1 MyCallable defines a method named call. Note that it doesn’t need to implement java.util.concurrent.Callable
2 we can call the method using the classic method call syntax
3 or we can omit .call thanks to the call operator

1.2.9. Operator precedence (TBD)

1.2.10. Operator overloading

Groovy allows you to overload the various operators so that they can be used with your own classes. Consider this simple class:

class Bucket {

    int size


    Bucket(int size) { this.size = size }


    Bucket plus(Bucket other) {                     (1)

        return new Bucket(this.size + other.size)

    }

}
1 Bucket implements a special method called plus()

Just by implementing the plus() method, the Bucket class can now be used with the + operator like so:

def b1 = new Bucket(4)

def b2 = new Bucket(11)

assert (b1 + b2).size == 15                         (1)
1 The two Bucket objects can be added together with the + operator

All (non-comparator) Groovy operators have a corresponding method that you can implement in your own classes. The only requirements are that your method is public, has the correct name, and has the correct number of arguments. The argument types depend on what types you want to support on the right hand side of the operator. For example, you could support the statement

assert (b1 + 11).size == 15

by implementing the plus() method with this signature:

Bucket plus(int capacity) {

    return new Bucket(this.size + capacity)

}

Here is a complete list of the operators and their corresponding methods:

Operator Method Operator Method

+

a.plus(b)

a[b]

a.getAt(b)

-

a.minus(b)

a[b] = c

a.putAt(b, c)

*

a.multiply(b)

<<

a.leftShift(b)

/

a.div(b)

>>

a.rightShift(b)

%

a.mod(b)

++

a.next()

**

a.power(b)

--

a.previous()

|

a.or(b)

+a

a.positive()

&

a.and(b)

-a

a.negative()

^

a.xor(b)

~a

a.bitwiseNegative()

1.3. Program structure (Anto Aravinth)

This chapter covers the program structure of the Groovy programming language.

1.3.1. Package name (TBD)

1.3.2. Imports (TBD)

Default imports (TBD)
Simple import (TBD)
Star import (TBD)
Static import (TBD)
Static star import (TBD)
Import aliasing (TBD)

1.3.3. Scripts versus classes

Package name plays exactly the same role as they used to play in Java. They allows us to separate the code base without any conflicts. Groovy classes must specify their package before the class definition, else the default package is assumed.

Defining a package is very similar to java, as you can see

//Defines a package named com.yoursite

package com.yoursite

To refer the class Foo in the com.yoursite.com package then you need to use com.yoursite.com.Foo in your code else you can use import statement as we can see now.

1.3.4. Imports

In order to refer any class you need a reference for it. Groovy follows Java’s notion of allowing import statement to resolve the class references. Groovy provides several builder classes, one such class is MarkupBuilder. MarkupBuilder is inside the package groovy.xml, in order to use this class, you need to import it as shown:

//imports the class MarkupBuilder

import groovy.xml.MarkupBuilder


//uses the imported class to create an object

def xml = new MarkupBuilder( )


assert xml != null
Default imports

Default imports are the imports that Groovy language provides by default. For example look at the following code:

new Date()

The same code in Java needs an import statement to Date class like this: import java.util.Date. Groovy by default imports these classes for you. There are six packages that groovy imports for you, they are:

import java.lang.*

import java.util.*

import java.io.*

import java.net.*

import groovy.lang.*

import groovy.util.*

import java.math.BigInteger

import java.math.BigDecimal
Simple import

Simple import is an import, where you fully define the class name along with the package. For example the import statement import groovy.xml.MarkupBuilder in the code below is the simple import, which directly refer the class inside the package.

//imports the class MarkupBuilder

import groovy.xml.MarkupBuilder


//uses the imported class to create an object

def xml = new MarkupBuilder( )


assert xml != null
Star import

Groovy (like Java) provides a special way to import all classes from a package using *, they are called as Star import. MarkupBuilder is class which is in package groovy.xml, another class called StreamingMarkupBuilder is in the same package. Consider the case, where you need to use two classes, then you can do:

import groovy.xml.MarkupBuilder

import groovy.xml.StreamingMarkupBuilder


def markupBuilder = new MarkupBuilder( )


assert markupBuilder != null


assert new StreamingMarkupBuilder() != null

Thats a perfectly valid code. But with * import, we can do the same like this:

import groovy.xml.*


def markupBuilder = new MarkupBuilder( )


assert markupBuilder != null


assert new StreamingMarkupBuilder() != null

where, does imports all the class under package groovy.xml. Using import can clutters your local namespace. But with groovy type aliasing, this can be solved easily.

Static import

Groovy’s static import capability allows you to reference imported classes as if they were static methods in your own class. This is similar to Java’s static import capability but works with Java 1.4 and above and is a little more dynamic than Java in that it allows you to define methods with the same name as an imported method as long as you have different types. If you have the same types, the imported class takes precedence. Here is a sample of its usage:

import static Boolean.FALSE


assert !FALSE //use directly, without Boolean prefix!

As you can see, now we can able to refer the static variable FALSE in our code base cleanly.

Static imports along with as keyword can make elegant solution. Consider you want to get the Calendar instance, using getInstance() method. Its a static method, so we can do a static import. But instead of calling getInstance() everytime, we can do this which can increase code readability:

import static Calendar.getInstance as now


assert now().class == Calendar.getInstance().class

Thats clean and sweet!

Static star import

Its very similar to the star import that we have seen earlier. It will import all the static methods from the given class. For example, lets say we need to calculate sin and cos for our application. The class java.lang.Math has static methods named sin,cos which fits our need. With help of static star import, we can do:

import static java.lang.Math.*


assert sin(0) == 0.0

assert cos(0) == 1.0

As you can see, we were able to access the methods sin,cos directly, instead like Math.sin.

Import aliasing

With type aliasing, we can refer to a fully qualified class name with the name of your choice. This can be done with as keyword in Groovy.

Consider the class with the following library code:

package thirdpartylib


public class MultiplyTwo {

    def static multiply(def value) {

        return value * 3 //intentionally wrong.

    }

}

and we have some existing code using this library:

def result = new MultiplyTwo().multiply(2)

Obviously, the result variable contains the wrong result. And assume you have used this library through out your code-base. Groovy has an elegant way to fix this issue.

With the type aliasing in place, we can fix the bug using the below code:

import thirdpartylib.MultiplyTwo as OrigMultiplyTwo

class MultiplyTwo extends OrigMultiplyTwo {

    def multiply(def value)

    {

        return value * 2 //corrected here

    }

}

// nothing changes below here

def multiplylib = new MultiplyTwo()


//assert passes as well

assert 4 == new MultiplyTwo().multiply(2)

Thats it! Notice how Groovy allowed us to use as keyword to solve the typical problem.

With Type aliasing we can able to solve the name collision problem ( which can occur with * imports) elegantly.

1.3.5. Initializers (TBD)

Static initializers (TBD)
Instance initializers (TBD)

1.4. Object orientation

This chapter covers the object orientation of the Groovy programming language.

1.4.1. Types (TBD)

Primitive types (TBD)
Class (TBD)
Normal class (TBD)
Static class (TBD)
Inner class (TBD)
Anonymous inner class (TBD)
Abstract class (TBD)
Interface (TBD)
Annotation (TBD)
Closure annotation parameters (TBD)
Meta-annotations (TBD)
Annotation placement (TBD)
Constructors (TBD)
Named argument constructor (TBD)
Methods (TBD)
Method definition (TBD)
Named arguments (TBD)
Default arguments (TBD)
Varargs (TBD)
Method selection algorithm (TBD)
Exception declaration (TBD)
Fields and properties (TBD)
Fields (TBD)
Properties (TBD)
Inheritance (TBD)
Generics (TBD)

1.4.2. Traits

Traits are a a structural construct of the language which allow:

  • composition of behaviors

  • runtime implementation of interfaces

  • behavior overriding

  • compatibility with static type checking/compilation

They can be seen as interfaces carrying both default implementations and state. A trait is defined using the trait keyword:

trait FlyingAbility {                           (1)

        String fly() { "I'm flying!" }          (2)

}
1 declaration of a trait
2 declaration of a method inside a trait

Then it can be used like a normal interface using the implements keyword:

class Bird implements FlyingAbility {}          (1)

def b = new Bird()                              (2)

assert b.fly() == "I'm flying!"                 (3)
1 Adds the trait FlyingAbility to the Bird class capabilities
2 instantiate a new Bird
3 the Bird class automatically gets the behavior of the FlyingAbility trait

Traits allow a wide range of capabilities, from simple composition to testing, which are described throughfully in this section.

Methods
Public methods

Declaring a method in a trait can be done like any regular method in a class:

trait FlyingAbility {                           (1)

        String fly() { "I'm flying!" }          (2)

}
1 declaration of a trait
2 declaration of a method inside a trait
Abstract methods

In addition, traits may declare abstract methods too, which therefore need to be implemented in the class implementing the trait:

trait Greetable {

    abstract String name()                              (1)

    String greeting() { "Hello, ${name()}!" }           (2)

}
1 implementing class will have to declare the name method
2 can be mixed with a concrete method

Then the trait can be used like this:

class Person implements Greetable {                     (1)

    String name() { 'Bob' }                             (2)

}


def p = new Person()

assert p.greeting() == 'Hello, Bob!'                    (3)
1 implement the trait Greetable
2 since name was abstract, it is required to implement it
3 then greeting can be called
Private methods

Traits may also define private methods. Those methods will not appear in the trait contract interface:

trait Greeter {

    private String greetingMessage() {                      (1)

        'Hello from a private method!'

    }

    String greet() {

        def m = greetingMessage()                           (2)

        println m

        m

    }

}

class GreetingMachine implements Greeter {}                 (3)

def g = new GreetingMachine()

assert g.greet() == "Hello from a private method!"          (4)

try {

    assert g.greetingMessage()                              (5)

} catch (MissingMethodException e) {

    println "greetingMessage is private in trait"

}
1 define a private method greetingMessage in the trait
2 the public greet message calls greetingMessage by default
3 create a class implementing the trait
4 greet can be called
5 but not greetingMessage
Traits only support public and private methods. Neither protected nor package private scopes are supported.
The meaning of this

this represents the implementing instance. Think of a trait as a superclass. This means that when you write:

trait Introspector {

    def whoAmI() { this }

}

class Foo implements Introspector {}

def foo = new Foo()

then calling:

foo.whoAmI()

will return the same instance:

assert foo.whoAmI().is(foo)
Interfaces

Traits may implement interfaces, in which case the interfaces are declared using the implements keyword:

interface Named {                                       (1)

    String name()

}

trait Greetable implements Named {                      (2)

    String greeting() { "Hello, ${name()}!" }

}

class Person implements Greetable {                     (3)

    String name() { 'Bob' }                             (4)

}


def p = new Person()

assert p.greeting() == 'Hello, Bob!'                    (5)

assert p instanceof Named                               (6)

assert p instanceof Greetable                           (7)
1 declaration of a normal interface
2 add Named to the list of implemented interfaces
3 declare a class that implements the Greetable trait
4 implement the missing greet method
5 the greeting implementation comes from the trait
6 make sure Person implements the Named interface
7 make sure Person implements the Greetable trait
Properties

A trait may define properties, like in the following example:

trait Named {

    String name                             (1)

}

class Person implements Named {}            (2)

def p = new Person(name: 'Bob')             (3)

assert p.name == 'Bob'                      (4)

assert p.getName() == 'Bob'                 (5)
1 declare a property name inside a trait
2 declare a class which implements the trait
3 the property is automatically made visible
4 it can be accessed using the regular property accessor
5 or using the regular getter syntax
Fields
Private fields

Since traits allow the use of private methods, it can also be interesting to use private fields to store state. Traits will let you do that:

trait Counter {

    private int count = 0                   (1)

    int count() { count += 1; count }       (2)

}

class Foo implements Counter {}             (3)

def f = new Foo()

assert f.count() == 1                       (4)
This is a major difference with Java 8 virtual extension methods. While virtual extension methods do not carry state, traits can. Also interesting traits in Groovy are supported starting with Java 6, but their implementation do not rely on virtual extension methods. This means that even if a trait can be seen from a Java class as a regular interface, this interface will not have default methods, only abstract ones.
Public fields

Public fields work the same way as private fields, but in order to avoid the diamond problem, field names are remapped in the implementing class:

trait Named {

    public String name                      (1)

}

class Person implements Named {}            (2)

def p = new Person()                        (3)

p.Named__name = 'Bob'                       (4)
1 declare a public field inside the trait
2 declare a class implementing the trait
3 create an instance of that class
4 the public field is available, but renamed

The name of the field depends on the fully qualified name of the trait. All dots (.) in package are replaced with an underscore (_), and the final name includes a double underscore. So if the type of the field is String, the name of the package is my.package, the name of the trait is Foo and the name of the field is bar, in the implementing class, the public field will appear as:

String my_package_Foo__bar
While traits support public fields, it is not recommanded to use them and considered as a bad practice.
Composition of behaviors

Traits can be used to implement multiple inheritance in a controlled way, avoiding the diamond issue. For example, we can have the following traits:

trait FlyingAbility {                           (1)

        String fly() { "I'm flying!" }          (2)

}

trait SpeakingAbility {

    String speak() { "I'm speaking!" }

}

And a class implementing both traits:

class Duck implements FlyingAbility, SpeakingAbility {} (1)


def d = new Duck()                                      (2)

assert d.fly() == "I'm flying!"                         (3)

assert d.speak() == "I'm speaking!"                     (4)
1 the Duck class implements both FlyingAbility and SpeakingAbility
2 creates a new instance of Duck
3 we can call the method fly from FlyingAbility
4 but also the method speak from SpeakingAbility

Traits encourage the reuse of capabilities among objects, and the creation of new classes by the composition of existing behavior.

Overriding default methods

Traits provide default implementations for methods, but it is possible to override them in the implementing class. For example, we can slightly change the example above, by having a duck which quacks:

class Duck implements FlyingAbility, SpeakingAbility {

    String quack() { "Quack!" }                         (1)

    String speak() { quack() }                          (2)

}


def d = new Duck()

assert d.fly() == "I'm flying!"                         (3)

assert d.quack() == "Quack!"                            (4)

assert d.speak() == "Quack!"                            (5)
1 define a method specific to Duck, named quack
2 override the default implementation of speak so that we use quack instead
3 the duck is still flying, from the default implementation
4 quack comes from the Duck class
5 speak no longer uses the default implementation from SpeakingAbility
Extending traits
Simple inheritance

Traits may extend another trait, in which case you must use the extends keyword:

trait Named {

    String name                                     (1)

}

trait Polite extends Named {                        (2)

    String introduce() { "Hello, I am $name" }      (3)

}

class Person implements Polite {}

def p = new Person(name: 'Alice')                   (4)

assert p.introduce() == 'Hello, I am Alice'         (5)
1 the Named trait defines a single name property
2 the Polite trait extends the Named trait
3 Polite adds a new method which has access to the name property of the super-trait
4 the name property is visible from the Person class implementing Polite
5 as is the introduce method
Multiple inheritance

Alternatively, a trait may extend multiple traits. In that case, all super traits must be declared in the implements clause:

trait WithId {                                      (1)

    Long id

}

trait WithName {                                    (2)

    String name

}

trait Identified implements WithId, WithName {}     (3)
1 WithId trait defines the id property
2 WithName trait defines the name property
3 Identified is a trait which inherits both WithId and WithName
Duck typing and traits
Dynamic code

Traits can call any dynamic code, like a normal Groovy class. This means that you can, in the body of a method, call methods which are supposed to exist in an implementing class, without having to explicitly declare them in an interface. This means that traits are fully compatible with duck typing:

trait SpeakingDuck {

    String speak() { quack() }                      (1)

}

class Duck implements SpeakingDuck {

    String methodMissing(String name, args) {

        "${name.capitalize()}!"                     (2)

    }

}

def d = new Duck()

assert d.speak() == 'Quack!'                        (3)
1 the SpeakingDuck expects the quack method to be defined
2 the Duck class does implement the method using methodMissing
3 calling the speak method triggers a call to quack which is handled by methodMissing
Dynamic methods in a trait

It is also possible for a trait to implement MOP methods like methodMissing or propertyMissing, in which case implementing classes will inherit the behavior from the trait, like in this example:

trait DynamicObject {                               (1)

    private Map props = [:]

    def methodMissing(String name, args) {

        name.toUpperCase()

    }

    def propertyMissing(String prop) {

        props['prop']

    }

    void setProperty(String prop, Object value) {

        props['prop'] = value

    }

}


class Dynamic implements DynamicObject {

    String existingProperty = 'ok'                  (2)

    String existingMethod() { 'ok' }                (3)

}

def d = new Dynamic()

assert d.existingProperty == 'ok'                   (4)

assert d.foo == null                                (5)

d.foo = 'bar'                                       (6)

assert d.foo == 'bar'                               (7)

assert d.existingMethod() == 'ok'                   (8)

assert d.someMethod() == 'SOMEMETHOD'               (9)
1 create a trait implementing several MOP methods
2 the Dynamic class defines a property
3 the Dynamic class defines a method
4 calling an existing property will call the method from Dynamic
5 calling an non-existing property will call the method from the trait
6 will call setProperty defined on the trait
7 will call getProperty defined on the trait
8 calling an existing method on Dynamic
9 but calling a non existing method thanks to the trait methodMissing
Multiple inheritance conflicts
Default conflict resolution

It is possible for a class to implement multiple traits. If some trait defines a method with the same signature as a method in another trait, we have a conflict:

trait A {

    String exec() { 'A' }               (1)

}

trait B {

    String exec() { 'B' }               (2)

}

class C implements A,B {}               (3)
1 trait A defines a method named exec returning a String
2 trait B defines the very same method
3 class C implements both traits

In this case, the default behavior is that methods from the last declared trait wins. Here, B is declared after A so the method from B will be picked up:

def c = new C()

assert c.exec() == 'B'
User conflict resolution

In case this behavior is not the one you want, you can explicitly choose which method to call using the Trait.super.foo syntax. In the example above, we can force to choose the method from trait A, by writing this:

class C implements A,B {

    String exec() { A.super.exec() }    (1)

}

def c = new C()

assert c.exec() == 'A'                  (2)
1 explicit call of exec from the trait A
2 calls the version from A instead of using the default resolution, which would be the one from B
Runtime implementation of traits
Implementing a trait at runtime

Groovy also supports implementing traits dynamically at runtime. It allows you to "decorate" an existing object using a trait. As an example, let’s start with this trait and the following class:

trait Extra {

    String extra() { "I'm an extra method" }            (1)

}

class Something {                                       (2)

    String doSomething() { 'Something' }                (3)

}
1 the Extra trait defines an extra method
2 the Something class does not implement the Extra trait
3 Something only defines a method doSomething

Then if we do:

def s = new Something()

s.extra()

the call to extra would fail because Something is not implementing Extra. It is possible to do it at runtime with the following syntax:

def s = new Something() as Extra                        (1)

s.extra()                                               (2)

s.doSomething()                                         (3)
1 use of the as keyword to coerce an object to a trait at runtime
2 then extra can be called on the object
3 and doSomething is still callable
When coercing an object to a trait, the result of the operation is not the same instance. It is guaranteed that the coerced object will implement both the trait and the interfaces that the original object implements, but the result will not be an instance of the original class.
Implementing multiple traits at once

Should you need to implement several traits at once, you can use the withTraits method instead of the as keyword:

trait A { void methodFromA() {} }

trait B { void methodFromB() {} }


class C {}


def c = new C()

c.methodFromA()                     (1)

c.methodFromB()                     (2)

def d = c.withTraits A, B           (3)

d.methodFromA()                     (4)

d.methodFromB()                     (5)
1 call to methodFromA will fail because C doesn’t implement A
2 call to methodFromB will fail because C doesn’t implement B
3 withTrait will wrap c into something which implements A and B
4 methodFromA will now pass because d implements A
5 methodFromB will now pass because d also implements B
When coercing an object to multiple traits, the result of the operation is not the same instance. It is guaranteed that the coerced object will implement both the traits and the interfaces that the original object implements, but the result will not be an instance of the original class.
Chaining behavior

Groovy supports the concept of stackable traits. The idea is to delegate from one trait to the other if the current trait is not capable of handling a message. To illustrate this, let’s imagine a message handler interface like this:

interface MessageHandler {

    void on(String message, Map payload)

}

Then you can compose a message handler by applying small behaviors. For example, let’s define a default handler in the form of a trait:

trait DefaultHandler implements MessageHandler {

    void on(String message, Map payload) {

        println "Received $message with payload $payload"

    }

}

Then any class can inherit the behavior of the default handler by implementing the trait:

class SimpleHandler implements DefaultHandler {}

Now what if you want to log all messages, in addition to the default handler? One option is to write this:

class SimpleHandlerWithLogging implements DefaultHandler {

    void on(String message, Map payload) {                                  (1)

        println "Seeing $message with payload $payload"                     (2)

        DefaultHandler.super.on(message, payload)                           (3)

    }

}
1 explicitly implement the on method
2 perform logging
3 continue by delegating to the DefaultHandler trait

This works but this approach has drawbacks:

  1. the logging logic is bound to a "concrete" handler

  2. we have an explicit reference to DefaultHandler in the on method, meaning that if we happen to change the trait that our class implements, code will be broken

As an alternative, we can write another trait which responsability is limited to logging:

trait LoggingHandler implements MessageHandler {                            (1)

    void on(String message, Map payload) {

        println "Seeing $message with payload $payload"                     (2)

        super.on(message, payload)                                          (3)

    }

}
1 the logging handler is itself a handler
2 prints the message it receives
3 then super makes it delegate the call to the next trait in the chain

Then our class can be rewritten as this:

class HandlerWithLogger implements DefaultHandler, LoggingHandler {}

def loggingHandler = new HandlerWithLogger()

loggingHandler.on('test logging', [:])

which will print:

Seeing test logging with payload [:]

Received test logging with payload [:]

As the priority rules imply that LoggerHandler wins because it is declared last, then a call to on will use the implementation from LoggingHandler. But the latter has a call to super, which means the next trait in the chain. Here, the next trait is DefaultHandler so both will be called:

The interest of this approach becomes more evident if we add a third handler, which is responsible for handling messages that start with say:

trait SayHandler implements MessageHandler {

    void on(String message, Map payload) {

        if (message.startsWith("say")) {                                    (1)

            println "I say ${message - 'say'}!"

        } else {

            super.on(message, payload)                                      (2)

        }

    }

}
1 a handler specific precondition
2 if the precondition is not meant, pass the message to the next handler in the chain

Then our final handler looks like this:

class Handler implements DefaultHandler, SayHandler, LoggingHandler {}

def h = new Handler()

h.on('foo', [:])

h.on('sayHello', [:])

Which means:

  • messages will first go through the logging handler

  • the logging handler calls super which will delegate to the next handler, which is the SayHandler

  • if the message starts with say, then the hanlder consumes the message

  • if not, the say handler delegates to the next handler in the chain

This approach is very powerful because it allows you to write handlers that do not know each other and yet let you combine them in the order you want. For example, if we execute the code, it will print:

Seeing foo with payload [:]

Received foo with payload [:]

Seeing sayHello with payload [:]

I say Hello!

but if we move the logging handler to be the second one in the chain, the output is different:

class AlternateHandler implements DefaultHandler, LoggingHandler, SayHandler {}

h = new AlternateHandler()

h.on('foo', [:])

h.on('sayHello', [:])

prints:

Seeing foo with payload [:]

Received foo with payload [:]

I say Hello!

The reason is that now, since the SayHandler consumes the message without calling super, the logging handler is not called anymore.

Semantics of super inside a trait

If a class implements multiple traits and that a call to an unqualified super is found, then:

  1. if the class implements another trait, the call delegates to the next trait in the chain

  2. if there isn’t any trait left in the chain, super refers to the super class of the implementing class (this)

For example, it is possible to decorate final classes thanks to this behavior:

trait Filtering {                                       (1)

    StringBuilder append(String str) {                  (2)

        def subst = str.replace('o','')                 (3)

        super.append(subst)                             (4)

    }

    String toString() { super.toString() }              (5)

}

def sb = new StringBuilder().withTraits Filtering       (6)

sb.append('Groovy')

assert sb.toString() == 'Grvy'                          (7)
1 define a trait named Filtering, supposed to be applied on a StringBuilder at runtime
2 redefine the append method
3 remove all 'o’s from the string
4 then delegate to super
5 in case toString is called, delegate to super.toString
6 runtime implementation of the Filtering trait on a StringBuilder instance
7 the string which has been appended no longer contains the letter o

In this example, when super.append is encountered, there is no other trait implemented by the target object, so the method which is called is the original append method, that is to say the one from StringBuilder. The same trick is used for toString, so that the string representation of the proxy object which is generated delegates to the toString of the StringBuilder instance.

Advanced features
SAM type coercion

If a trait defines a single abstract method, it is candidate for SAM type coercion. For example, imagine the following trait:

trait Greeter {

    String greet() { "Hello $name" }        (1)

    abstract String getName()               (2)

}
1 the greet method is not abstract and calls the abstract method getName
2 getName is an abstract method

Since getName is the single abstract method in the Greeter trait, you can write:

Greeter greeter = { 'Alice' }               (1)
1 the closure "becomes" the implementation of the getName single abstract method

or even:

void greet(Greeter g) { println g.greet() } (1)

greet { 'Alice' }                           (2)
1 the greet method accepts the SAM type Greeter as parameter
2 we can call it directly with a closure
Differences with Java 8 default methods

In Java 8, interfaces can have default implementations of methods. If a class implements an interface and does not provide an implementation for a default method, then the implementation from the interface is chosen. Traits behave the same but with a major difference: the implementation from the trait is always used if the class declares the trait in its interface list and that it doesn’t provide an implementation.

This feature can be used to compose behaviors in an very precise way, in case you want to override the behavior of an already implemented method.

To illustrate the concept, let’s start with this simple example:

import groovy.transform.CompileStatic

import org.codehaus.groovy.control.CompilerConfiguration

import org.codehaus.groovy.control.customizers.ASTTransformationCustomizer

import org.codehaus.groovy.control.customizers.ImportCustomizer


class SomeTest extends GroovyTestCase {

    def config

    def shell


    void setup() {

        config = new CompilerConfiguration()

        shell = new GroovyShell(config)

    }

    void testSomething() {

        assert shell.evaluate('1+1') == 2

    }

    void otherTest() { /* ... */ }

}

In this example, we create a simple test case which uses two properties (config and shell) and uses those in multiple test methods. Now imagine that you want to test the same, but with another distinct compiler configuration. One option is to create a subclass of SomeTest:

class AnotherTest extends SomeTest {

    void setup() {

        config = new CompilerConfiguration()

        config.addCompilationCustomizers( ... )

        shell = new GroovyShell(config)

    }

}

It works, but what if you have actually multiple test classes, and that you want to test the new configuration for all those test classes? Then you would have to create a distinct subclass for each test class:

class YetAnotherTest extends SomeTest {

    void setup() {

        config = new CompilerConfiguration()

        config.addCompilationCustomizers( ... )

        shell = new GroovyShell(config)

    }

}

Then what you see is that the setup method of both tests is the same. The idea, then, is to create a trait:

trait MyTestSupport {

    void setup() {

        config = new CompilerConfiguration()

        config.addCompilationCustomizers( new ASTTransformationCustomizer(CompileStatic) )

        shell = new GroovyShell(config)

    }

}

Then use it in the subclasses:

class AnotherTest extends SomeTest implements MyTestSupport {}

class YetAnotherTest extends SomeTest2 implements MyTestSupport {}

...

It would allow us to dramatically reduce the boilerplate code, and reduces the risk of forgetting to change the setup code in case we decide to change it. Even if setup is already implemented in the super class, since the test class declares the trait in its interface list, the behavior will be borrowed from the trait implementation!

This feature is in particular useful when you don’t have access to the super class source code. It can be used to mock methods or force a particular implementation of a method in a subclass. It lets you refactor your code to keep the overriden logic in a single trait and inherit a new behavior just by implementing it. The alternative, of course, is to override the method in every place you would have used the new code.

It’s worth noting that if you use runtime traits, the methods from the trait are always preferred to those of the proxied object:
class Person {

    String name                                         (1)

}

trait Bob {

    String getName() { 'Bob' }                          (2)

}


def p = new Person(name: 'Alice')

assert p.name == 'Alice'                                (3)

def p2 = p as Bob                                       (4)

assert p2.name == 'Bob'                                 (5)
1 the Person class defines a name property which results in a getName method
2 Bob is a trait which defines getName as returning Bob
3 the default object will return Alice
4 p2 coerces p into Bob at runtime
5 getName returns Bob because getName is taken from the trait
Again, don’t forget that dynamic trait coercion returns a distinct object which only implements the original interfaces, as well as the traits.
Differences with mixins

There are several conceptual differences with mixins, as they are available in Groovy. Note that we are talking about runtime mixins, not the @Mixin annotation which is deprecated in favour of traits.

First of all, methods defined in a trait are visible in bytecode:

  • internally, the trait is represented as an interface (without default methods) and several helper classes

  • this means that an object implementing a trait effectively implements an interface

  • those methods are visible from Java

  • they are compatible with type checking and static compilation

Methods added through a mixin are, on the contrary, only visible at runtime:

class A { String methodFromA() { 'A' } }        (1)

class B { String methodFromB() { 'B' } }        (2)

A.metaClass.mixin B                             (3)

def o = new A()

assert o.methodFromA() == 'A'                   (4)

assert o.methodFromB() == 'B'                   (5)

assert o instanceof A                           (6)

assert !(o instanceof B)                        (7)
1 class A defines methodFromA
2 class B defines methodFromB
3 mixin B into A
4 we can call methodFromA
5 we can also call methodFromB
6 the object is an instance of A
7 but it’s not an instanceof B

The last point is actually a very important and illustrates a place where mixins have an advantage over traits: the instances are not modified, so if you mixin some class into another, there isn’t a third class generated, and methods which respond to A will continue responding to A even if mixed in.

Static methods, properties and fields
The following instructions are subject to caution. Static member support is work in progress and still experimental. The information below is valid for 2.3.6 only.

It is possible to define static methods in a trait, but it comes with numerous limitations:

  • traits with static methods cannot be compiled statically or type checked. All static methods/properties/field are accessed dynamically (it’s a limitation from the JVM).

  • the trait is interpreted as a template for the implementing class, which means that each implementing class will get its own static methods/properties/methods. So a static member declared on a trait doesn’t belong to the Trait, but to it’s implementing class.

Let’s start with a simple example:

trait TestHelper {

    public static boolean CALLED = false        (1)

    static void init() {                        (2)

        CALLED = true                           (3)

    }

}

class Foo implements TestHelper {}

Foo.init()                                      (4)

assert Foo.TestHelper__CALLED                   (5)
1 the static field is declared in the trait
2 a static method is also declared in the trait
3 the static field is updated within the trait
4 a static method init is made available to the implementing class
5 the static field is remapped to avoid the diamond issue

As usual, it is not recommanded to use public fields. Anyway, should you want this, you must understand that the following code would fail:

Foo.CALLED = true

because there is no static field CALLED defined on the trait itself. Likewise, if you have two distinct implementing classes, each one gets a distinct static field:

class Bar implements TestHelper {}              (1)

class Baz implements TestHelper {}              (2)

Bar.init()                                      (3)

assert Bar.TestHelper__CALLED                   (4)

assert !Baz.TestHelper__CALLED                  (5)
1 class Bar implements the trait
2 class Baz also implements the trait
3 init is only called on Bar
4 the static field CALLED on Bar is updated
5 but the static field CALLED on Baz is not, because it is distinct
Inheritance of state gotchas

We have seen that traits are stateful. It is possible for a trait to define fields or properties, but when a class implements a trait, it gets those fields/properties on a per-trait basis. So consider the following example:

trait IntCouple {

    int x = 1

    int y = 2

    int sum() { x+y }

}

The trait defines two properties, x and y, as well as a sum method. Now let’s create a class which implements the trait:

class BaseElem implements IntCouple {

    int f() { sum() }

}

def base = new BaseElem()

assert base.f() == 3

The result of calling f is 3, because f delegates to sum in the trait, which has state. But what if we write this instead?

class Elem implements IntCouple {

    int x = 3                                       (1)

    int y = 4                                       (2)

    int f() { sum() }                               (3)

}

def elem = new Elem()
1 Override property x
2 Override property y
3 Call sum from trait

If you call elem.f(), what is the expected output? Actually it is:

assert elem.f() == 3

The reason is that the sum method accesses the fields of the trait. So it is using the x and y values defined in the trait. If you want to use the values from the implementing class, then you need to derefencence fields by using getters and setters, like in this last example:

trait IntCouple {

    int x = 1

    int y = 2

    int sum() { getX()+getY() }

}


class Elem implements IntCouple {

    int x = 3

    int y = 4

    int f() { sum() }

}

def elem = new Elem()

assert elem.f() == 7
Limitations
Compatibility with AST transformations
Traits are not officially compatible with AST transformations. Some of them, like @CompileStatic will be applied on the trait itself (not on implementing classes), while others will apply on both the implementing class and the trait. There is absolutely no guarantee that an AST transformation will run on a trait as it does on a regular class, so use it at your own risk!
Prefix and postfix operations

Within traits, prefix and postfix operations are not allowed if they update a field of the trait:

trait Counting {

    int x

    void inc() {

        x++                             (1)

    }

    void dec() {

        --x                             (2)

    }

}

class Counter implements Counting {}

def c = new Counter()

c.inc()
1 x is defined within the trait, postfix increment is not allowed
2 x is defined within the trait, prefix decrement is not allowed

A workaround is to use the += operator instead. :leveloffset: 0

1.4.3. Closures (TBD)

This chapter covers Groovy Closures.

Syntax (TBD)
Parameters (TBD)
Normal parameters (TBD)
Implicit parameter (TBD)
Default parameter (TBD)
Varargs (TBD)
Delegation strategy (TBD)
Closures in GStrings (TBD)
Functional programming (TBD)
Currying (TBD)
Left currying (TBD)
Right currying (TBD)
Index based currying (TBD)
Memoization (TBD)
Composition (TBD)
Trampoline (TBD)

1.4.4. Semantics

This chapter covers the semantics of the Groovy programming language.

Statements
Variable definition

Variables can be defined using either their type (like String) or by using the keyword def:

String x

def o

def is a replacement for a type name. In variable definitions it is used to indicate that you don’t care about the type. In variable definitions it is mandatory to either provide a type name explicitly or to use "def" in replacement. This is needed to the make variable definitions detectable for the Groovy parser.

You can think of def as an alias of Object and you will understand it in an instant.

Variable definition types can be refined by using generics, like in List<String> names. To learn more about the generics support, please read the generics section.
Variable assignment

You can assign values to variables for later use. Try the following:

x = 1

println x


x = new java.util.Date()

println x


x = -3.1499392

println x


x = false

println x


x = "Hi"

println x
Multiple assignment

Groovy supports multiple assignment, i.e. where multiple variables can be assigned at once, e.g.:

def (a, b, c) = [10, 20, 'foo']

assert a == 10 && b == 20 && c == 'foo'

You can provide types as part of the declaration if you wish:

def (int i, String j) = [10, 'foo']

assert i == 10 && j == 'foo'

As well as used when declaring variables it also applies to existing variables:

def nums = [1, 3, 5]

def a, b, c

(a, b, c) = nums

assert a == 1 && b == 3 && c == 5

The syntax works for arrays as well as lists, as well as methods that return either of these:

def (_, month, year) = "18th June 2009".split()

assert "In $month of $year" == 'In June of 2009'
Overflow and Underflow

If the left hand side has too many variables, excess ones are filled with null’s:

def (a, b, c) = [1, 2]

assert a == 1 && b == 2 && c == null

If the right hand side has too many variables, the extra ones are ignored:

def (a, b) = [1, 2, 3]

assert a == 1 && b == 2
Object destructuring with multiple assignment

In the section describing the various Groovy operators, the case of the subscript operator has been covered, explaining how you can override the getAt()/putAt() method.

With this technique, we can combine multiple assignments and the subscript operator methods to implement object destructuring.

Consider the following immutable Coordinates class, containing a pair of longitude and latitude doubles, and notice our implementation of the getAt() method:

@Immutable

class Coordinates {

    double latitude

    double longitude


    double getAt(int idx) {

        if (idx == 0) latitude

        else if (idx == 1) longitude

        else throw new Exception("Wrong coordinate index, use 0 or 1")

    }

}

Now let’s instantiate this class and destructure its longitude and latitude:

def coordinates = new Coordinates(latitude: 43.23, longitude: 3.67) (1)


def (la, lo) = coordinates                                          (2)


assert la == 43.23                                                  (3)

assert lo == 3.67
1 we create an instance of the Coordinates class
2 then, we use a multiple assignment to get the individual longitude and latitude values
3 and we can finally assert their values.
Control structures (WIP)
Conditional structures
if / else

Groovy supports the usual if - else syntax from Java

def x = false

def y = false


if ( !x ) {

    x = true

}


assert x == true


if ( x ) {

    x = false

} else {

    y = true

}


assert x == y

Groovy also supports the normal Java "nested" if then else if syntax:

if ( ... ) {

    ...

} else if (...) {

    ...

} else {

    ...

}
switch / case

The switch statement in Groovy is backwards compatible with Java code; so you can fall through cases sharing the same code for multiple matches.

One difference though is that the Groovy switch statement can handle any kind of switch value and different kinds of matching can be performed.

def x = 1.23

def result = ""


switch ( x ) {

    case "foo":

        result = "found foo"

        // lets fall through


    case "bar":

        result += "bar"


    case [4, 5, 6, 'inList']:

        result = "list"

        break


    case 12..30:

        result = "range"

        break


    case Integer:

        result = "integer"

        break


    case Number:

        result = "number"

        break


    case ~/fo*/: // toString() representation of x matches the pattern?

        result = "foo regex"

        break


    case { it < 0 }: // or { x < 0 }

        result = "negative"

        break


    default:

        result = "default"

}


assert result == "number"

Switch supports the following kinds of comparisons:

  • Class case values matches if the switch value is an instance of the class

  • Regular expression case values match if the toString() representation of the switch value matches the regex

  • Collection case values match if the switch value is contained in the collection. This also includes ranges (since they are Lists)

  • Closure case values match if the calling the closure returns a result which is true according to the Groovy truth

  • If none of the above are used then the case value matches if the case value equals the switch value

default must go at the end of the switch/case. While in Java the default can be placed anywhere in the switch/case, the default in Groovy is used more as an else than assigning a default case.
when using a closure case value, the default it parameter is actually the switch value (in our example, variable x)
Looping structures
Classic for loop

Groovy supports the standard Java / C for loop:

String message = ''

for (int i = 0; i < 5; i++) {

    message += 'Hi '

}

assert message == 'Hi Hi Hi Hi Hi '
for in loop

The for loop in Groovy is much simpler and works with any kind of array, collection, Map, etc.

// iterate over a range

def x = 0

for ( i in 0..9 ) {

    x += i

}

assert x == 45


// iterate over a list

x = 0

for ( i in [0, 1, 2, 3, 4] ) {

    x += i

}

assert x == 10


// iterate over an array

def array = (0..4).toArray()

x = 0

for ( i in array ) {

    x += i

}

assert x == 10


// iterate over a map

def map = ['abc':1, 'def':2, 'xyz':3]

x = 0

for ( e in map ) {

    x += e.value

}

assert x == 6


// iterate over values in a map

x = 0

for ( v in map.values() ) {

    x += v

}

assert x == 6


// iterate over the characters in a string

def text = "abc"

def list = []

for (c in text) {

    list.add(c)

}

assert list == ["a", "b", "c"]
Groovy also supports the Java colon variation with colons: for (char c : text) {}, where the type of the variable is mandatory.
while loop

Groovy supports the usual while {…} loops like Java:

def x = 0

def y = 5


while ( y-- > 0 ) {

    x++

}


assert x == 5
Exception handling

Exception handling is the same as Java.

try / catch / finally

You can specify a complete try-catch-finally, a try-catch, or a try-finally set of blocks.

Braces are required around each block’s body.
try {

    'moo'.toLong()   // this will generate an exception

    assert false     // asserting that this point should never be reached

} catch ( e ) {

    assert e in NumberFormatException

}

We can put code within a finally clause following a matching try clause, so that regardless of whether the code in the try clause throws an exception, the code in the finally clause will always execute:

def z

try {

    def i = 7, j = 0

    try {

        def k = i / j

        assert false        //never reached due to Exception in previous line

    } finally {

        z = 'reached here'  //always executed even if Exception thrown

    }

} catch ( e ) {

    assert e in ArithmeticException

    assert z == 'reached here'

}
Multi-catch

With the multi catch block (since Groovy 2.0), we’re able to define several exceptions to be catch and treated by the same catch block:

try {

    /* ... */

} catch ( IOException | NullPointerException e ) {

    /* one block to handle 2 exceptions */

}
Power assertion (TBD)
Labeled statements (TBD)
Expressions (TBD)
GPath expressions (TBD)
Promotion and coercion (TBD)
Number promotion (TBD)
Closure to type coercion
Assigning a closure to a SAM type

A SAM type is a type which defines a single abstract method. This includes:

Functional interfaces
interface Predicate<T> {

    boolean accept(T obj)

}
Abstract classes with single abstract method
abstract class Greeter {

    abstract String getName()

    void greet() {

        println "Hello, $name"

    }

}

Any closure can be converted into a SAM type using the as operator:

Predicate filter = { it.contains 'G' } as Predicate

assert filter.accept('Groovy') == true


Greeter greeter = { 'Groovy' } as Greeter

greeter.greet()

However, the as Type expression is optional since Groovy 2.2.0. You can omit it and simply write:

Predicate filter = { it.contains 'G' }

assert filter.accept('Groovy') == true


Greeter greeter = { 'Groovy' }

greeter.greet()

which means you are also allowed to use method pointers, as shown in the following example:

boolean doFilter(String s) { s.contains('G') }


Predicate filter = this.&doFilter

assert filter.accept('Groovy') == true


Greeter greeter = GroovySystem.&getVersion

greeter.greet()
Calling a method accepting a SAM type with a closure

The second and probably more important use case for closure to SAM type coercion is calling a method which accepts a SAM type. Imagine the following method:

public <T> List<T> filter(List<T> source, Predicate<T> predicate) {

    source.findAll { predicate.accept(it) }

}

Then you can call it with a closure, without having to create an explicit implementation of the interface:

assert filter(['Java','Groovy'], { it.contains 'G'} as Predicate) == ['Groovy']

But since Groovy 2.2.0, you are also able to omit the explicit coercion and call the method as if it used a closure:

assert filter(['Java','Groovy']) { it.contains 'G'} == ['Groovy']

As you can see, this has the advantage of letting you use the closure syntax for method calls, that is to say put the closure outside of the parenthesis, improving the readability of your code.

Closure to arbitrary type coercion

In addition to SAM types, a closure can be coerced to any type and in particular interfaces. Let’s define the following interface:

interface FooBar {

    int foo()

    void bar()

}

You can coerce a closure into the interface using the as keyword:

def impl = { println 'ok'; 123 } as FooBar

This produces a class for which all methods are implemented using the closure:

assert impl.foo() == 123

impl.bar()

But it is also possible to coerce a closure to any class. For example, we can replace the interface that we defined with class without changing the assertions:

class FooBar {

    int foo() { 1 }

    void bar() { println 'bar' }

}


def impl = { println 'ok'; 123 } as FooBar


assert impl.foo() == 123

impl.bar()
Map to type coercion

Usually using a single closure to implement an interface or a class with multiple methods is not the way to go. As an alternative, Groovy allows you to coerce a map into an interface or a class. In that case, keys of the map are interpreted as method names, while the values are the method implementation. The following example illustrates the coercion of a map into an Iterator:

def map

map = [

  i: 10,

  hasNext: { map.i > 0 },

  next: { map.i-- },

]

def iter = map as Iterator

Of course this is a rather contrived example, but illustrates the concept. You only need to implement those methods that are actually called, but if a method is called that doesn’t exist in the map a MissingMethodException or an UnsupportedOperationException is thrown, depending on the arguments passed to the call, as in the following example:

interface X {

    void f()

    void g(int n)

    void h(String s, int n)

}


x = [ f: {println "f called"} ] as X

x.f() // method exists

x.g() // MissingMethodException here

x.g(5) // UnsupportedOperationException here

The type of the exception depends on the call itself:

  • MissingMethodException if the arguments of the call do not match those from the interface/class

  • UnsupportedOperationException if the arguments of the call match one of the overloaded methods of the interface/class

String to enum coercion

Groovy allows transparent String (or GString) to enum values coercion. Imagine you define the following enum:

enum State {

    up,

    down

}

then you can assign a string to the enum without having to use an explicit as coercion:

State st = 'up'

assert st == State.up

It is also possible to use a GString as the value:

def val = "up"

State st = "${val}"

assert st == State.up

However, this would throw a runtime error (IllegalArgumentException):

State st = 'not an enum value'

Note that it is also possible to use implicit coercion in switch statements:

State switchState(State st) {

    switch (st) {

        case 'up':

            return State.down // explicit constant

        case 'down':

            return 'up' // implicit coercion for return types

    }

}

in particular, see how the case use string constants. But if you call a method that uses an enum with a String argument, you still have to use an explicit as coercion:

assert switchState('up' as State) == State.down

assert switchState(State.down) == State.up
Custom type coercion

It is possible for a class to define custom coercion strategies by implementing the asType method. Custom coercion is invoked using the as operator and is never implicit. As an example, imagine you defined two classes, Polar and Cartesian, like in the following example:

class Polar {

    double r

    double phi

}

class Cartesian {

   double x

   double y

}

And that you want to convert from polar coordinates to cartesian coordinates. One way of doing this is to define the asType method in the Polar class:

def asType(Class target) {

    if (Cartesian==target) {

        return new Cartesian(x: r*cos(phi), y: r*sin(phi))

    }

}

which allows you to use the as coercion operator:

def sigma = 1E-16

def polar = new Polar(r:1.0,phi:PI/2)

def cartesian = polar as Cartesian

assert abs(cartesian.x-sigma) < sigma

Putting it all together, the Polar class looks like this:

class Polar {

    double r

    double phi

    def asType(Class target) {

        if (Cartesian==target) {

            return new Cartesian(x: r*cos(phi), y: r*sin(phi))

        }

    }

}

but it is also possible to define asType outside of the Polar class, which can be practical if you want to define custom coercion strategies for "closed" classes or classes for which you don’t own the source code, for example using a metaclass:

Polar.metaClass.asType = { Class target ->

    if (Cartesian==target) {

        return new Cartesian(x: r*cos(phi), y: r*sin(phi))

    }

}
Class literals vs variables and the as operator

Using the as keyword is only possible if you have a static reference to a class, like in the following code:

interface Greeter {

    void greet()

}

def greeter = { println 'Hello, Groovy!' } as Greeter // Greeter is known statically

greeter.greet()

But what if you get the class by reflection, for example by calling Class.forName?

Class clazz = Class.forName('Greeter')

Trying to use the reference to the class with the as keyword would fail:

greeter = { println 'Hello, Groovy!' } as clazz

// throws:

// unable to resolve class clazz

// @ line 9, column 40.

//   greeter = { println 'Hello, Groovy!' } as clazz

It is failing because the as keyword only works with class literals. Instead, you need to call the asType method:

greeter = { println 'Hello, Groovy!' }.asType(clazz)

greeter.greet()
Optionality (TBD)
Optional parentheses (TBD)
Optional semicolons (TBD)
Optional return keyword (TBD)
Optional public keyword (TBD)
The Groovy Truth (TBD)
Customizing the truth with asBoolean() methods (TBD)
Typing (WIP)
Optional typing

Optional typing is the idea that a program can work even if you don’t put an explicit type on a variable. Being a dynamic language, Groovy naturally implements that feature, for example when you declare a variable:

String aString = 'foo'                      (1)

assert aString.toUpperCase()                (2)
1 foo is declared using an explicit type, String
2 we can call the toUpperCase method on a String

Groovy will let you write this instead:

def aString = 'foo'                         (1)

assert aString.toUpperCase()                (2)
1 foo is declared using def
2 we can still call the toUpperCase method, because the type of aString is resolved at runtime

So it doesn’t matter that you use an explicit type here. It is in particular interesting when you combine this feature with static type checking, because the type checker performs type inference.

Likewise, Groovy doesn’t make it mandatory to declare the types of a parameter in a method:

String concat(String a, String b) {

    a+b

}

assert concat('foo','bar') == 'foobar'

can be rewritten using def as both return type and parameter types, in order to take advantage of duck typing, as illustrated in this example:

def concat(def a, def b) {                              (1)

    a+b

}

assert concat('foo','bar') == 'foobar'                  (2)

assert concat(1,2) == 3                                 (3)
1 both the return type and the parameter types use def
2 it makes it possible to use the method with String
3 but also with int`s since the `plus method is defined
Using the def keyword here is recommanded to describe the intent of a method which is supposed to work on any type, but technically, we could use Object instead and the result would be the same: def is, in Groovy, strictly equivalent to using Object.

Eventually, the type can be removed altogether from both the return type and the descriptor. But if you want to remove it from the return type, you then need to add an explicit modifier for the method, so that the compiler can make a difference between a method declaration and a method call, like illustrated in this example:

private concat(a,b) {                                   (1)

    a+b

}

assert concat('foo','bar') == 'foobar'                  (2)

assert concat(1,2) == 3                                 (3)
1 if we want to omit the return type, an explicit modifier has to be set.
2 it is still possible to use the method with String
3 and also with `int`s
Omitting types is in general considered a bad practice in method parameters or method return types for public APIs. While using def in a local variable is not really a problem because the visibility of the variable is limited to the method itself, while set on a method parameter, def will be converted to Object in the method signature, making it difficult for users to know which is the expected type of the arguments. This means that you should limit this to cases where you are explicitly relying on duck typing.
Static type checking

By default, Groovy performs minimal type checking at compile time. Since it is primarily a dynamic language, most checks that a static compiler would normally do aren’t possible at compile time. A method added via runtime metaprogramming might alter a class or object’s runtime behavior. Let’s illustrate why in the following example:

class Person {                                                          (1)

    String firstName

    String lastName

}

def p = new Person(firstName: 'Raymond', lastName: 'Devos')             (2)

assert p.formattedName == 'Raymond Devos'                               (3)
1 the Person class only defines two properties, firstName and lastName
2 we can create an instance of Person
3 and call a method named formattedName

It is quite common in dynamic languages for code such as the above example not to throw any error. How can this be? In Java, this would typically fail at compile time. However, in Groovy, it will not fail at compile time, and if coded correctly, will also not fail at runtime. In fact, to make this work at runtime, one possibility is to rely on runtime metaprogramming. So just adding this line after the declaration of the Person class is enough:

Person.metaClass.getFormattedName = { "$delegate.firstName $delegate.lastName" }

This means that in general, in Groovy, you can’t make any assumption about the type of an object beyond its declaration type, and even if you know it, you can’t determine at compile time what method will be called, or which property will be retrieved, and this is perfectly fine. This is how dynamic languages work, and it has a lot of interest.

However, if your program doesn’t rely on dynamic features and that you come from the static world (in particular, from a Java mindset), not catching such "errors" at compile time can be surprising. As we have seen in the previous example, the compiler cannot be sure this is an error. To make it aware that it is, you have to explicitly instruct the compiler that you are switching to a type checked mode. This can be done by annotating a class or a method with @groovy.lang.TypeChecked.

When type checking is activated, the compiler performs much more work:

  • type inference is activated, meaning that even if you use def on a local variable for example, the type checker will be able to infer the type of the variable from the assignments

  • method calls are resolved at compile time, meaning that if a method is not declared on a class, the compiler will throw an error

  • in general, all the compile time errors that you are used to find in a static language will appear: method not found, property not found, incompatible types for method calls, number precision errors, …

In this section, we will describe the behavior of the type checker in various situations and explain the limits of using @TypeChecked on your code.

The @TypeChecked annotation
Activating type checking at compile time

The groovy.lang.TypeChecked annotation enabled type checking. It can be placed on a class:

@groovy.transform.TypeChecked

class Calculator {

    int sum(int x, int y) { x+y }

}

Or on a method:

class Calculator {

    @groovy.transform.TypeChecked

    int sum(int x, int y) { x+y }

}

In the first case, all methods, properties, fields, inner classes, … of the annotated class will be type checked, whereas in the second case, only the method and potential closures or anonymous inner classes that it contains will be type checked.

Skipping sections

The scope of type checking can be restricted. For example, if a class is type checked, you can instruct the type checker to skip a method by annotating it with @TypeChecked(TypeCheckingMode.SKIP):

import groovy.transform.TypeChecked

import groovy.transform.TypeCheckingMode


@TypeChecked                                        (1)

class GreetingService {

    String greeting() {                             (2)

        doGreet()

    }


    @TypeChecked(TypeCheckingMode.SKIP)             (3)

    private String doGreet() {

        def b = new SentenceBuilder()

        b.Hello.my.name.is.John                     (4)

        b

    }

}

def s = new GreetingService()

assert s.greeting() == 'Hello my name is John'
1 the GreetingService class is marked as type checked
2 so the greeting method is automatically type checked
3 but doGreet is marked with SKIP
4 the type checker doesn’t complain about missing properties here

In the previous example, SentenceBuilder relies on dynamic code. There’s no real Hello method or property, so the type checker would normally complain and compilation would fail. Since the method that uses the builder is marked with TypeCheckingMode.SKIP, type checking is skipped for this method, so the code will compile, even if the rest of the class is type checked.

The following sections describe the semantics of type checking in Groovy.

Type checking assignments

An object o of type A can be assigned to a variable of type T if and only if:

  • T equals A

    Date now = new Date()
  • or T is one of String, boolean, Boolean or Class

    String s = new Date() // implicit call to toString
    
    Boolean boxed = 'some string'       // Groovy truth
    
    boolean prim = 'some string'        // Groovy truth
    
    Class clazz = 'java.lang.String'    // class coercion
  • or o is null and T is not a primitive type

    String s = null         // passes
    
    int i = null            // fails
  • or T is an array and A is an array and the component type of A is assignable to the component type of B

    int[] i = new int[4]        // passes
    
    int[] i = new String[4]     // fails
  • or T is an array and A is a list and the component type of A is assignable to the component type of B

    int[] i = [1,2,3]               // passes
    
    int[] i = [1,2, new Date()]     // fails
  • or T is a superclass of A

    AbstractList list = new ArrayList()     // passes
    
    LinkedList list = new ArrayList()       // fails
  • or T is an interface implemented by A

    List list = new ArrayList()             // passes
    
    RandomAccess list = new LinkedList()    // fails
  • or T or A are a primitive type and their boxed types are assignable

    int i = 0
    
    Integer bi = 1
    
    int x = new Integer(123)
    
    double d = new Float(5f)
  • or T extends groovy.lang.Closure and A is a SAM-type (single abstract method type)

    Runnable r = { println 'Hello' }
    
    interface SAMType {
    
        int doSomething()
    
    }
    
    SAMType sam = { 123 }
    
    assert sam.doSomething() == 123
    
    abstract class AbstractSAM {
    
        int calc() { 2* value() }
    
        abstract int value()
    
    }
    
    AbstractSAM c = { 123 }
    
    assert c.calc() == 246
  • or T and A derive from java.lang.Number and conform to the following table

Table 2. Number types (java.lang.XXX)
T A Examples

Double

Any but BigDecimal or BigInteger

Double d1 = 4d

Double d2 = 4f

Double d3 = 4l

Double d4 = 4i

Double d5 = (short) 4

Double d6 = (byte) 4

Float

Any type but BigDecimal, BigInteger or Double

Float f1 = 4f

Float f2 = 4l

Float f3 = 4i

Float f4 = (short) 4

Float f5 = (byte) 4

Long

Any type but BigDecimal, BigInteger, Double or Float

Long l1 = 4l

Long l2 = 4i

Long l3 = (short) 4

Long l4 = (byte) 4

Integer

Any type but BigDecimal, BigInteger, Double, Float or Long

Integer i1 = 4i

Integer i2 = (short) 4

Integer i3 = (byte) 4

Short

Any type but BigDecimal, BigInteger, Double, Float, Long or Integer

Short s1 = (short) 4

Short s2 = (byte) 4

Byte

Byte

Byte b1 = (byte) 4
List and map constructors

In addition to the assignment rules above, if an assignment is deemed invalid, in type checked mode, a list literal or a map literal A can be assigned to a variable of type T if:

  • the assignment is a variable declaration and A is a list literal and T has a constructor whose parameters match the types of the elements in the list literal

  • the assignment is a variable declaration and A is a map literal and T has a no-arg constructor and a property for each of the map keys

For example, instead of writing:

@groovy.transform.TupleConstructor

class Person {

    String firstName

    String lastName

}

Person classic = new Person('Ada','Lovelace')

You can use a "list constructor":

Person list = ['Ada','Lovelace']

or a "map constructor":

Person map = [firstName:'Ada', lastName:'Lovelace']

If you use a map constructor, additional checks are done on the keys of the map to check if a property of the same name is defined. For example, the following will fail at compile time:

@groovy.transform.TupleConstructor

class Person {

    String firstName

    String lastName

}

Person map = [firstName:'Ada', lastName:'Lovelace', age: 24]     (1)
1 The type checker will throw an error No such property: age for class: Person at compile time
Method resolution

In type checked mode, methods are resolved at compile time. Resolution works by name and arguments. The return type is irrelevant to method selection. Types of arguments are matched against the types of the parameters following those rules:

An argument o of type A can be used for a parameter of type T if and only if:

  • T equals A

    int sum(int x, int y) {
    
        x+y
    
    }
    
    assert sum(3,4) == 7
  • or T is a String and A is a GString

    String format(String str) {
    
        "Result: $str"
    
    }
    
    assert format("${3+4}") == "Result: 7"
  • or o is null and T is not a primitive type

    String format(int value) {
    
        "Result: $value"
    
    }
    
    assert format(7) == "Result: 7"
    
    format(null)           // fails
  • or T is an array and A is an array and the component type of A is assignable to the component type of B

    String format(String[] values) {
    
        "Result: ${values.join(' ')}"
    
    }
    
    assert format(['a','b'] as String[]) == "Result: a b"
    
    format([1,2] as int[])              // fails
  • or T is a superclass of A

    String format(AbstractList list) {
    
        list.join(',')
    
    }
    
    format(new ArrayList())              // passes
    
    String format(LinkedList list) {
    
        list.join(',')
    
    }
    
    format(new ArrayList())              // fails
  • or T is an interface implemented by A

    String format(List list) {
    
        list.join(',')
    
    }
    
    format(new ArrayList())                  // passes
    
    String format(RandomAccess list) {
    
        'foo'
    
    }
    
    format(new LinkedList())                 // fails
  • or T or A are a primitive type and their boxed types are assignable

    int sum(int x, Integer y) {
    
        x+y
    
    }
    
    assert sum(3, new Integer(4)) == 7
    
    assert sum(new Integer(3), 4) == 7
    
    assert sum(new Integer(3), new Integer(4)) == 7
    
    assert sum(new Integer(3), 4) == 7
  • or T extends groovy.lang.Closure and A is a SAM-type (single abstract method type)

    interface SAMType {
    
        int doSomething()
    
    }
    
    int twice(SAMType sam) { 2*sam.doSomething() }
    
    assert twice { 123 } == 246
    
    abstract class AbstractSAM {
    
        int calc() { 2* value() }
    
        abstract int value()
    
    }
    
    int eightTimes(AbstractSAM sam) { 4*sam.calc() }
    
    assert eightTimes { 123 } == 984
  • or T and A derive from java.lang.Number and conform to the same rules as assignment of numbers

If a method with the appropriate name and arguments is not found at compile time, an error is thrown. The difference with "normal" Groovy is illustrated in the following example:

class MyService {

    void doSomething() {

        printLine 'Do something'            (1)

    }

}
1 printLine is an error, but since we’re in a dynamic mode, the error is not caught at compile time

The example above shows a class that Groovy will be able to compile. However, if you try to create an instance of MyService and call the doSomething method, then it will fail at runtime, because printLine doesn’t exist. Of course, we already showed how Groovy could make this a perfectly valid call, for example by catching MethodMissingException or implementing a custom meta-class, but if you know you’re not in such a case, @TypeChecked comes handy:

class MyService {

    void doSomething() {

        printLine 'Do something'            (1)

    }

}
1 printLine is this time a compile-time error

Just adding @TypeChecked will trigger compile time method resolution. The type checker will try to find a method printLine accepting a String on the MyService class, but cannot find one. It will fail compilation with the following message:

Cannot find matching method MyService#printLine(java.lang.String)

It is important to understand the logic behind the type checker: it is a compile-time check, so by definition, the type checker is not aware of any kind of runtime metaprogramming that you do. This means that code which is perfectly valid without @TypeChecked will not compile anymore if you activate type checking. This is in particular true if you think of duck typing:
class Duck {

    void quack() {              (1)

        println 'Quack!'

    }

}

class QuackingBird {

    void quack() {              (2)

        println 'Quack!'

    }

}

@groovy.transform.TypeChecked

void accept(quacker) {

    quacker.quack()             (3)

}

accept(new Duck())              (4)
1 we define a Duck class which defines a quack method
2 we define another QuackingBird class which also defines a quack method
3 quacker is loosely typed, so since the method is @TypeChecked, we will obtain a compile-time error
4 even if in non type-checked Groovy, this would have passed

There are possible workarounds, like introducing an interface, but basically, by activating type checking, you gain type safety but you loose some features of the language. Hopefully, Groovy introduces some features like flow typing to reduce the gap between type-checked and non type-checked Groovy.

Type inference
Principles

When code is annotated with @TypeChecked, the compiler performs type inference. It doesn’t simply rely on static types, but also uses various techniques to infer the types of variables, return types, literals, … so that the code remains as clean as possible even if you activate the type checker.

The simplest example is infering the type of a variable:

def message = 'Welcome to Groovy!'              (1)

println message.toUpperCase()                   (2)

println message.upper() // compile time error   (3)
1 a variable is declared using the def keyword
2 calling toUpperCase is allowed by the type checker
3 calling upper will fail at compile time

The reason the call to toUpperCase works is because the type of message was inferred as being a String.

Variables vs fields in type inference

It is worth noting that although the compiler performs type inference on local variables, it does not perform any kind of type inference on fields, always falling back to the declared type of a field. To illustrate this, let’s take a look at this example:

class SomeClass {

    def someUntypedField                                                                (1)

    String someTypedField                                                               (2)


    void someMethod() {

        someUntypedField = '123'                                                        (3)

        someUntypedField = someUntypedField.toUpperCase()  // compile-time error        (4)

    }


    void someSafeMethod() {

        someTypedField = '123'                                                          (5)

        someTypedField = someTypedField.toUpperCase()                                   (6)

    }


    void someMethodUsingLocalVariable() {

        def localVariable = '123'                                                       (7)

        someUntypedField = localVariable.toUpperCase()                                  (8)

    }

}
1 someUntypedField uses def as a declaration type
2 someTypedField uses String as a declaration type
3 we can assign anything to someUntypedField
4 yet calling toUpperCase fails at compile time because the field is not typed properly
5 we can assign a String to a field of type String
6 and this time toUpperCase is allowed
7 if we assign a String to a local variable
8 then calling toUpperCase is allowed on the local variable

Why such a difference? The reason is thread safety. At compile time, we can’t make any guarantee about the type of a field. Any thread can access any field at any time and between the moment a field is assigned a variable of some type in a method and the time is is used the line after, another thread may have changed the contents of the field. This is not the case for local variables: we know if they "escape" or not, so we can make sure that the type of a variable is constant (or not) over time. Note that even if a field is final, the JVM makes no guarantee about it, so the type checker doesn’t behave differently if a field is final or not.

This is one of the reasons why we recommend to use typed fields. While using def for local variables is perfectly fine thanks to type inference, this is not the case for fields, which also belong to the public API of a class, hence the type is important.
Collection literal type inference

Groovy provides a syntax for various type literals. There are three native collection literals in Groovy:

  • lists, using the [] literal

  • maps, using the [:] literal

  • ranges, using the (..,..) literal

The inferred type of a literal depends on the elements of the literal, as illustrated in the following table:

Literal Inferred type
def list = []

java.util.List

def list = ['foo','bar']

java.util.List<String>

def list = ["${foo}","${bar}"]

java.util.List<GString> be careful, a GString is not a String!

def map = [:]

java.util.LinkedHashMap

def map1 = [someKey: 'someValue']

def map2 = ['someKey': 'someValue']

java.util.LinkedHashMap<String,String>

def map1 = [someKey: 'someValue']

def map2 = ['someKey': 'someValue']

java.util.LinkedHashMap<GString,String> be careful, the key is a GString!

def intRange = (0..10)

groovy.lang.IntRange

def charRange = ('a'..'z')

groovy.lang.Range<String> : uses the type of the bounds to infer the component type of the range

As you can see, with the noticeable exception of the IntRange, the inferred type makes use of generics types to describe the contents of a collection. In case the collection contains elements of different types, the type checker still performs type inference of the components, but uses the notion of least upper bound.

Least upper bound

In Groovy, the least upper bound of two types A and B is defined as a type which:

  • superclass corresponds to the common super class of A and B

  • interfaces correspond to the interfaces implemented by both A and B

  • if A or B is a primitive type and that A isn’t equal to B, the least upper bound of A and B is the least upper bound of their wrapper types

If A and B only have one (1) interface in common and that their common superclass is Object, then the LUB of both is the common interface.

The least upper bound represents the minimal type to which both A and B can be assigned. So for example, if A and B are both String, then the LUB (least upper bound) of both is also String.

class Top {}

class Bottom1 extends Top {}

class Bottom2 extends Top {}


assert leastUpperBound(String, String) == String                    (1)

assert leastUpperBound(ArrayList, LinkedList) == AbstractList       (2)

assert leastUpperBound(ArrayList, List) == List                     (3)

assert leastUpperBound(List, List) == List                          (4)

assert leastUpperBound(Bottom1, Bottom2) == Top                     (5)

assert leastUpperBound(List, Serializable) == Object                (6)
1 the LUB of String and String is String
2 the LUB of ArrayList and LinkedList is their common super type, AbstractList
3 the LUB of ArrayList and List is their only common interface, List
4 the LUB of two identical interfaces is the interface itself
5 the LUB of Bottom1 and Bottom2 is their superclass Top
6 the LUB of two types which have nothing in common is Object

In those examples, the LUB is always representable as a normal, JVM supported, type. But Groovy internally represents the LUB as a type which can be more complex, and that you wouldn’t be able to use to define a variable for example. To illustrate this, let’s continue with this example:

interface Foo {}

class Top {}

class Bottom extends Top implements Serializable, Foo {}

class SerializableFooImpl implements Serializable, Foo {}

What is the least upper bound of Bottom and SerializableFooImpl? They don’t have a common super class (apart from Object), but they do share 2 interfaces (Serializable and Foo), so their least upper bound is a type which represents the union of two interfaces (Serializable and Foo). This type cannot be defined in the source code, yet Groovy knows about it.

In the context of collection type inference (and generic type inference in general), this becomes handy, because the type of the components is inferred as the least upper bound. We can illustrate why this is important in the following example:

interface Greeter { void greet() }                  (1)

interface Salute { void salute() }                  (2)


class A implements Greeter, Salute {                (3)

    void greet() { println "Hello, I'm A!" }

    void salute() { println "Bye from A!" }

}

class B implements Greeter, Salute {                (4)

    void greet() { println "Hello, I'm B!" }

    void salute() { println "Bye from B!" }

    void exit() { println 'No way!' }               (5)

}

def list = [new A(), new B()]                       (6)

list.each {

    it.greet()                                      (7)

    it.salute()                                     (8)

    it.exit()                                       (9)

}
1 the Greeter interface defines a single method, greet
2 the Salute interface defines a single method, salute
3 class A implements both Greeter and Salute but there’s no explicit interface extending both
4 same for B
5 but B defines an additional exit method
6 the type of list is inferred as "list of the LUB of A and B"
7 so it is possible to call greet which is defined on both A and B through the Greeter interface
8 and it is possible to call salut which is defined on both A and B through the Salut interface
9 yet calling exit is a compile time error because it doesn’t belong to the LUB of A and B (only defined in B)

The error message will look like:

[Static type checking] - Cannot find matching method Greeter or Salute#exit()

which indicates that the exit method is neither defines on Greeter nor Salute, which are the two interfaces defined in the least upper bound of A and B.

instanceof inference

In normal, non type checked, Groovy, you can write things like:

class Greeter {

    String greeting() { 'Hello' }

}


void doSomething(def o) {

    if (o instanceof Greeter) {     (1)

        println o.greeting()        (2)

    }

}


doSomething(new Greeter())
1 guard the method call with an instanceof check
2 make the call

The method call works because of dynamic dispatch (the method is selected at runtime). The equivalent code in Java would require to cast o to a Greeter before calling the greeting method, because methods are selected at compile time:

if (o instanceof Greeter) {

    System.out.println(((Greeter)o).greeting());

}

However, in Groovy, even if you add @TypeChecked (and thus activate type checking) on the doSomething method, the cast is not necessary. The compiler embeds instanceof inference that makes the cast optional.

Flow typing

Flow typing is an important concept of Groovy in type checked mode and an extension of type inference. The idea is that the compiler is capable of infering the type of variables in the flow of the code, not just at initialization:

@groovy.transform.TypeChecked

void flowTyping() {

    def o = 'foo'                       (1)

    o = o.toUpperCase()                 (2)

    o = 9d                              (3)

    o = Math.sqrt(o)                    (4)

}
1 first, o is declared using def and assigned a String
2 the compiler inferred that o is a String, so calling toUpperCase is allowed
3 o is reassigned with a double
4 calling Math.sqrt passes compilation because the compiler knows that at this point, o is a double

So the type checker is aware of the fact that the concrete type of a variable is different over time. In particular, if you replace the last assignment with:

o = 9d

o = o.toUpperCase()

The type checker will now fail at compile time, because it knows that o is a double when toUpperCase is called, so it’s a type error.

It is important to understand that it is not the fact of declaring a variable with def that triggers type inference. Flow typing works for any variable of any type. Declaring a variable with an explicit type only constraints what you can assign to a variable:

@groovy.transform.TypeChecked

void flowTypingWithExplicitType() {

    List list = ['a','b','c']           (1)

    list = list*.toUpperCase()          (2)

    list = 'foo'                        (3)

}
1 list is declared as an unchecked List and assigned a list literal of `String`s
2 this line passes compilation because of flow typing: the type checker knows that list is at this point a List<String>
3 but you can’t assign a String to a List so this is a type checking error

You can also note that even if the variable is declared without generics information, the type checker knows what is the component type. Therefore, such code would fail compilation:

@groovy.transform.TypeChecked

void flowTypingWithExplicitType() {

    List list = ['a','b','c']           (1)

    list.add(1)                         (2)

}
1 list is inferred as List<String>
2 so adding an int to a List<String> is a compile-time error

Fixing this requires adding an explicit generic type to the declaration:

@groovy.transform.TypeChecked

void flowTypingWithExplicitType() {

    List<? extends Serializable> list = []                      (1)

    list.addAll(['a','b','c'])                                  (2)

    list.add(1)                                                 (3)

}
1 list declared as List<? extends Serializable> and initialized with an empty list
2 elements added to the list conform to the declaration type of the list
3 so adding an int to a List<? extends Serializable> is allowed

Flow typing has been introduced to reduce the difference in semantics between classic and static Groovy. In particular, consider the behavior of this code in Java:

public Integer compute(String str) {

    return str.length();

}

public String compute(Object o) {

    return "Nope";

}

// ...

Object string = "Some string";          (1)

Object result = compute(string);        (2)

System.out.println(result);             (3)
1 o is declared as an Object and assigned a String
2 we call the compute method with o
3 and print the result

In Java, this code will output 0, because method selection is done at compile time and based on the declared types. So even if o is a String at runtime, it is still the Object version which is called, because o has been declared as an Object. To be short, in Java, declared types are most important, be it variable types, parameter types or return types.

In Groovy, we could write:

int compute(String string) { string.length() }

String compute(Object o) { "Nope" }

Object o = 'string'

def result = compute(o)

println result

But this time, it will return 6, because the method which is chosen is chosen at runtime, based on the actual argument types. So at runtime, o is a String so the String variant is used. Note that this behavior has nothing to do with type checking, it’s the way Groovy works in general: dynamic dispatch.

In type checked Groovy, we want to make sure the type checker selects the same method at compile time, that the runtime would choose. It is not possible in general, due to the semantics of the language, but we can make things better with flow typing. With flow typing, o is inferred as a String when the compute method is called, so the version which takes a String and returns an int is chosen. This means that we can infer the return type of the method to be an int, and not a String. This is important for subsequent calls and type safety.

So in type checked Groovy, flow typing is a very important concept, which also implies that if @TypeChecked is applied, methods are selected based on the inferred types of the arguments, not on the declared types. This doesn’t ensure 100% type safety, because the type checker may select a wrong method, but it ensures the closest semantics to dynamic Groovy.

Advanced type inference

A combination of flow typing and least upper bound inference is used to perform advanced type inference and ensure type safety in multiple situations. In particular, program control structures are likely to alter the inferred type of a variable:

class Top {

   void methodFromTop() {}

}

class Bottom extends Top {

   void methodFromBottom() {}

}

def o

if (someCondition) {

    o = new Top()                               (1)

} else {

    o = new Bottom()                            (2)

}

o.methodFromTop()                               (3)

o.methodFromBottom()  // compilation error      (4)
1 if someCondition is true, o is assigned a Top
2 if someCondition is false, o is assigned a Bottom
3 calling methodFromTop is safe
4 but calling methodFromBottom is not, so it’s a compile time error

When the type checker visits an if/else control structure, it checks all variables which are assigned in if/else branches and computes the least upper bound of all assignments. This type is the type of the inferred variable after the if/else block, so in this example, o is assigned a Top in the if branch and a Bottom in the else branch. The LUB of those is a Top, so after the conditional branches, the compiler infers o as being a Top. Calling methodFromTop will therefore be allowed, but not methodFromBottom.

The same reasoning exists with closures and in particular closure shared variables. A closure shared variable is a variable which is defined outside of a closure, but used inside a closure, as in this example:

def text = 'Hello, world!'                          (1)

def closure = {

    println text                                    (2)

}
1 a variable named text is declared
2 text is used from inside a closure. It is a closure shared variable.

Groovy allows developers to use those variables without requiring them to be final. This means that a closure shared variable can be reassigned inside a closure:

String result

doSomething { String it ->

    result = "Result: $it"

}

result = result?.toUpperCase()

The problem is that a closure is an independent block of code that can be executed (or not) at any time. In particular, doSomething may be asynchronous, for example. This means that the body of a closure doesn’t belong to the main control flow. For that reason, the type checker also computes, for each closure shared variable, the LUB of all assignments of the variable, and will use that LUB as the inferred type outside of the scope of the closure, like in this example:

class Top {

   void methodFromTop() {}

}

class Bottom extends Top {

   void methodFromBottom() {}

}

def o = new Top()                               (1)

Thread.start {

    o = new Bottom()                            (2)

}

o.methodFromTop()                               (3)

o.methodFromBottom()  // compilation error      (4)
1 a closure-shared variable is first assigned a Top
2 inside the closure, it is assigned a Bottom
3 methodFromTop is allowed
4 methodFromBottom is a compilation error

Here, it is clear that when methodFromBottom is called, there’s no guarantee, at compile-time or runtime that the type of o will effectively be a Bottom. There are chances that it will be, but we can’t make sure, because it’s asynchronous. So the type checker will only allow calls on the least upper bound, which is here a Top.

Closures and type inference

The type checker performs special inference on closures, resulting on additional checks on one side and improved fluency on the other side.

Return type inference

The first thing that the type checker is capable of doing is infering the return type of a closure. This is simply illustrated in the following example:

@groovy.transform.TypeChecked

int testClosureReturnTypeInference(String arg) {

    def cl = { "Arg: $arg" }                                (1)

    def val = cl()                                          (2)


    val.length()                                            (3)

}
1 a closure is defined, and it returns a string (more precisely a GString)
2 we call the closure and assign the result to a variable
3 the type checker inferred that the closure would return a string, so calling length() is allowed

As you can see, unlike a method which declares its return type explicitly, there’s no need to declare the return type of a closure: its type is inferred from the body of the closure.

Closures vs methods

It’s worth noting that return type inference is only applicable to closures. While the type checker could do the same on a method, it is in practice not desirable: in general, methods can be overriden and it is not statically possible to make sure that the method which is called is not an overriden version. So flow typing would actually think that a method returns something, while in reality, it could return something else, like illustrated in the following example:

@TypeChecked

class A {

    def compute() { 'some string' }             (1)

    def computeFully() {

        compute().toUpperCase()                 (2)

    }

}

@TypeChecked

class B extends A {

    def compute() { 123 }                       (3)

}
1 class A defines a method compute which effectively returns a String
2 this will fail compilation because the return type of compute is def(aka Object)
3 class B extends A and redefines compute, this type returning an int

As you can see, if the type checker relied on the inferred return type of a method, with flow typing, the type checker could determine that it is ok to call toUpperCase. It is in fact an error, because a subclass can override compute and return a different object. Here, B#compute returns an int, so someone calling computeFully on an instance of B would see a runtime error. The compiler prevents this from happening by using the declared return type of methods instead of the inferred return type.

For consistency, this behavior is the same for every method, even if they are static or final.

Parameter type inference

In addition to the return type, it is possible for a closure to infer its parameter types from the context. There are two ways for the compiler to infer the parameter types:

  • through implicit SAM type coercion

  • through API metadata

To illustrate this, lets start with an example that will fail compilation due to the inability for the type checker to infer the parameter types:

class Person {

    String name

    int age

}


void inviteIf(Person p, Closure<Boolean> predicate) {           (1)

    if (predicate.call(p)) {

        // send invite

        // ...

    }

}


@groovy.transform.TypeChecked

void failCompilation() {

    Person p = new Person(name: 'Gerard', age: 55)

    inviteIf(p) {                                               (2)

        it.age >= 18 // No such property: age                   (3)

    }

}
1 the inviteIf method accepts a Person and a Closure
2 we call it with a Person and a Closure
3 yet it is not statically known as being a Person and compilation fails

In this example, the closure body contains it.age. With dynamic, not type checked code, this would work, because the type of it would be a Person at runtime. Unfortunately, at compile-time, there’s no way to know what is the type of it, just by reading the signature of inviteIf.

Explicit closure parameters

To be short, the type checker doesn’t have enough contextual information on the inviteIf method to determine statically the type of it. This means that the method call needs to be rewritten like this:

inviteIf(p) { Person it ->                                  (1)

    it.age >= 18

}
1 the type of it needs to be declared explicitly

By explicitly declaring the type of the it variable, you can workaround the problem and make this code statically checked.

Parameters inferred from single-abstract method types

For an API or framework designer, there are two ways to make this more elegant for users, so that they don’t have to declare an explicit type for the closure parameters. The first one, and easiest, is to replace the closure with a SAM type:

interface Predicate<On> { boolean apply(On e) }                 (1)


void inviteIf(Person p, Predicate<Person> predicate) {          (2)

    if (predicate.apply(p)) {

        // send invite

        // ...

    }

}


@groovy.transform.TypeChecked

void passesCompilation() {

    Person p = new Person(name: 'Gerard', age: 55)


    inviteIf(p) {                                               (3)

        it.age >= 18                                            (4)

    }

}
1 declare a SAM interface with an apply method
2 inviteIf now uses a Predicate<Person> instead of a Closure<Boolean>
3 there’s no need to declare the type of the it variable anymore
4 it.age compiles properly, the type of it is inferred from the Predicate#apply method signature
By using this technique, we leverage the automatic coercion of closures to SAM types feature of Groovy. The question whether you should use a SAM type or a Closure really depends on what you need to do. In a lot of cases, using a SAM interface is enough, especially if you consider functional interfaces as they are found in Java 8. However, closures provide features that are not accessible to functional interfaces. In particular, closures can have a delegate, and owner and can be manipulated as objects (for example, cloned, serialized, curried, …) before being called. They can also support multiple signatures (polymorphism). So if you need that kind of manipulation, it is preferable to switch to the most advanced type inference annotations which are described below.

The original issue that needs to be solved when it comes to closure parameter type inference, that is to say, statically determining the types of the arguments of a closure without having to have them explicitly declared, is that the Groovy type system inherits the Java type system, which is insufficient to describe the types of the arguments.

The @ClosureParams annotation

Groovy provides an annotation, @ClosureParams which is aimed at completing type information. This annotation is primarily aimed at framework and API developers who want to extend the capabilities of the type checker by providing type inference metadata. This is important if your library makes use of closures and that you want the maximum level of tooling support too.

Let’s illustrate this by fixing the original example, introducing the @ClosureParams annotation:

import groovy.transform.stc.ClosureParams

import groovy.transform.stc.FirstParam

void inviteIf(Person p, @ClosureParams(FirstParam) Closure<Boolean> predicate) {        (1)

    if (predicate.call(p)) {

        // send invite

        // ...

    }

}

inviteIf(p) {                                                                       (2)

    it.age >= 18

}
1 the closure parameter is annotated with @ClosureParams
2 it’s not necessary to use an explicit type for it, which is inferred

The @ClosureParams annotation minimally accepts one argument, which is named a type hint. A type hint is a class which is reponsible for completing type information at compile time for the closure. In this example, the type hint being used is groovy.transform.stc.FirstParam which indicated to the type checker that the closure will accept one parameter whose type is the type of the first parameter of the method. In this case, the first parameter of the method is Person, so it indicates to the type checker that the first parameter of the closure is in fact a Person.

The second argument is optional and named options. It’s semantics depends on the type hint class. Groovy comes with various bundled type hints, illustrated in the table below:

Table 3. Predefined type hints
Type hint Polymorphic? Description and examples

FirstParam
SecondParam
ThirdParam

No

The first (resp. second, third) parameter type of the method

import groovy.transform.stc.FirstParam

void doSomething(String str, @ClosureParams(FirstParam) Closure c) {

    c(str)

}

doSomething('foo') { println it.toUpperCase() }
import groovy.transform.stc.SecondParam

void withHash(String str, int seed, @ClosureParams(SecondParam) Closure c) {

    c(31*str.hashCode()+seed)

}

withHash('foo', (int)System.currentTimeMillis()) {

    int mod = it%2

}
import groovy.transform.stc.ThirdParam

String format(String prefix, String postfix, String o, @ClosureParams(ThirdParam) Closure c) {

    "$prefix${c(o)}$postfix"

}

assert format('foo', 'bar', 'baz') {

    it.toUpperCase()

} == 'fooBAZbar'

FirstParam.FirstGenericType
SecondParam.FirstGenericType
ThirdParam.FirstGenericType

No

The first generic type of the first (resp. second, third) parameter of the method

import groovy.transform.stc.FirstParam

public <T> void doSomething(List<T> strings, @ClosureParams(FirstParam.FirstGenericType) Closure c) {

    strings.each {

        c(it)

    }

}

doSomething(['foo','bar']) { println it.toUpperCase() }

doSomething([1,2,3]) { println(2*it) }

Variants for SecondGenericType and ThirdGenericType exist for all FirstParam, SecondParam and ThirdParam type hints.

SimpleType

No

A type hint for which the type of closure parameters comes from the options string.

import groovy.transform.stc.SimpleType

public void doSomething(@ClosureParams(value=SimpleType,options=['java.lang.String','int']) Closure c) {

    c('foo',3)

}

doSomething { str, len ->

    assert str.length() == len

}

This type hint supports a single signature and each of the parameter is specified as a value of the options array using a fully-qualified type name or a primitive type.

MapEntryOrKeyValue

Yes

A dedicated type hint for closures that either work on a Map.Entry single parameter, or two parameters corresponding to the key and the value.

import groovy.transform.stc.MapEntryOrKeyValue

public <K,V> void doSomething(Map<K,V> map, @ClosureParams(MapEntryOrKeyValue) Closure c) {

    // ...

}

doSomething([a: 'A']) { k,v ->

    assert k.toUpperCase() == v.toUpperCase()

}

doSomething([abc: 3]) { e ->

    assert e.key.length() == e.value

}

This type hint requires that the first argument is a Map type, and infers the closure parameter types from the map actual key/value types.

FromAbstractTypeMethods

Yes

Infers closure parameter types from the abstract method of some type. A signature is inferred for each abstract method.

import groovy.transform.stc.FromAbstractTypeMethods

abstract class Foo {

    abstract void firstSignature(int x, int y)

    abstract void secondSignature(String str)

}

void doSomething(@ClosureParams(value=FromAbstractTypeMethods, options=["Foo"]) Closure cl) {

    // ...

}

doSomething { a, b -> a+b }

doSomething { s -> s.toUpperCase() }

If there are multiple signatures like in the example above, the type checker will only be able to infer the types of the arguments if the arity of each method is different. In the example above, firstSignature takes 2 arguments and secondSignature takes 1 argument, so the type checker can infer the argument types based on the number of arguments.

FromString

Yes

Infers the closure parameter typs from the options argument. The options argument consists of an array of comma-separated non-primitive types. Each element of the array corresponds to a single signature, and each comma in an element separate parameters of the signature. In short, this is the most generic type hint, and each string of the options map is parsed as if it was a signature literal. While being very powerful, this type hint must be avoided if you can because it increases the compilation times due to the necessity of parsing the type signatures.

A single signature for a closure accepting a String:

import groovy.transform.stc.FromString

void doSomething(@ClosureParams(value=FromString, options=["String","String,Integer"]) Closure cl) {

    // ...

}

doSomething { s -> s.toUpperCase() }

doSomething { s,i -> s.toUpperCase()*i }

A polymorphic closure, accepting either a String or a String, Integer:

import groovy.transform.stc.FromString

void doSomething(@ClosureParams(value=FromString, options=["String","String,Integer"]) Closure cl) {

    // ...

}

doSomething { s -> s.toUpperCase() }

doSomething { s,i -> s.toUpperCase()*i }

A polymorphic closure, accepting either a T or a pair T,T:

import groovy.transform.stc.FromString

public <T> void doSomething(T e, @ClosureParams(value=FromString, options=["T","T,T"]) Closure cl) {

    // ...

}

doSomething('foo') { s -> s.toUpperCase() }

doSomething('foo') { s1,s2 -> assert s1.toUpperCase() == s2.toUpperCase() }
Even though you use FirstParam, SecondParam or ThirdParam as a type hint, it doesn’t stricly mean that the argument which will be passed to the closure will be the first (resp. second, third) argument of the method call. It only means that the type of the parameter of the closure will be the same as the type of the first (resp. second, third) argument of the method call.

In short, the lack of the @ClosureParams annotation on a method accepting a Closure will not fail compilation. If present (and it can be present in Java sources as well as Groovy sources), then the type checker has more information and can perform additional type inference. This makes this feature particularily interesting for framework developers.

@DelegatesTo

The @DelegatesTo annotation is used by the type checker to infer the type of the delegate. It allows the API designer to instruct the compiler what is the type of the delegate and the delegation strategy. The @DelegatesTo annotation is discussed in a specific section.

Static compilation (WIP)
Dynamic vs static

In the type checking section, we have seen that Groovy provides optional type checking thanks to the @TypeChecked annotation. The type checker runs at compile time and performs a static analysis of dynamic code. The program will behave exactly the same whether type checking has been enabled or not. This means that the @TypeChecked annotation is neutral with regards to the semantics of a program. Even though it may be necessary to add type information in the sources so that the program is considered type safe, in the end, the semantics of the program are the same.

While this may sound fine, there is actually one issue with this: type checking of dynamic code, done at compile time, is by definition only correct if no runtime specific behavior occurs. For example, the following program passes type checking:

class Computer {

    int compute(String str) {

        str.length()

    }

    String compute(int x) {

        String.valueOf(x)

    }

}


@groovy.transform.TypeChecked

void test() {

    def computer = new Computer()

    computer.with {

        assert compute(compute('foobar')) =='6'

    }

}

There are two compute methods. One accepts a String and returns an int, the other accepts an int and returns a String. If you compile this, it is considered type safe: the inner compute('foobar') call will return an int, and calling compute on this int will in turn return a String.

Now, before calling test(), consider adding the following line:

Computer.metaClass.compute = { String str -> new Date() }

Using runtime metaprogramming, we’re actually modifying the behavior of the compute(String) method, so that instead of returning the length of the provided argument, it will return a Date. If you execute the program, it will fail at runtime. Since this line can be added from anywhere, in any thread, there’s absolutely no way for the type checker to statically make sure that no such thing happens. In short, the type checker is vulnerable to monkey patching. This is just one example, but this illustrates the concept that doing static analysis of a dynamic program is inherently wrong.

The Groovy language provides an alternative annotation to @TypeChecked which will actually make sure that the methods which are inferred as being called will effectively be called at runtime. This annotation turns the Groovy compiler into a static compiler, where all method calls are resolved at compile time and the generated bytecode makes sure that this happens: the annotation is @groovy.transform.CompileStatic.

The @CompileStatic annotation

The @CompileStatic annotation can be added anywhere the @TypeChecked annotation can be used, that is to say on a class or a method. It is not necessary to add both @TypeChecked and @CompileStatic, as @CompileStatic performs everything @TypeChecked does, but in addition triggers static compilation.

Let’s take the example which failed, but this time let’s replace the @TypeChecked annotation with @CompileStatic:

class Computer {

    int compute(String str) {

        str.length()

    }

    String compute(int x) {

        String.valueOf(x)

    }

}


@groovy.transform.CompileStatic

void test() {

    def computer = new Computer()

    computer.with {

        assert compute(compute('foobar')) =='6'

    }

}

Computer.metaClass.compute = { String str -> new Date() }

run()

This is the only difference. If we execute this program, this time, there is no runtime error. The test method became immune to monkey patching, because the compute methods which are called in its body are linked at compile time, so even if the metaclass of Computer changes, the program still behaves as expected by the type checker.

Key benefits

There are several benefits of using @CompileStatic on your code:

The performance improvements depend on the kind of program you are executing. It it is I/O bound, the difference between statically compiled code and dynamic code is barely noticeable. On highly CPU intensive code, since the bytecode which is generated is very close, if not equal, to the one that Java would produce for an equivalent program, the performance is greatly improved.

Using the invokedynamic version of Groovy, which is accessible to people using JDK 7 and above, the performance of the dynamic code should be very close to the performance of statically compiled code. Sometimes, it can even be faster! There is only one way to determine which version you should choose: measuring. The reason is that depending on your program and the JVM that you use, the performance can be significantly different. In particular, the invokedynamic version of Groovy is very sensitive to the JVM version in use.