// 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 */
其实与java的注释都差不多了,写代码笔者建议多写写注释
/**
* 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 a greeting message
*/
String greet(String otherPerson) {
"Hello ${otherPerson}"
}
}
与java 不一样的是,groovy支持运行时的doc,意识就是在运行时,可以通过代码获取到类的注释文本
默认情况,Groovydoc是禁用的,如果你想要开启,可以通过jvm选项启动
-Dgroovy.attach.runtime.groovydoc=true
笔者是觉得这个特性目前是没有太大用处,但是作为一种新特性,肯定是有一点点用处,说不定以后注释就和注解一样,有着特定的意义与功能,代码如下。
/**@
* Some class groovydoc for Foo
*/
class Foo {
/**@
* Some method groovydoc for bar
*/
void bar() {
}
}
assert Foo.class.groovydoc.content.contains('Some class groovydoc for Foo')
assert Foo.class.getMethod('bar', new Class[0]).groovydoc.content.contains('Some method groovydoc for bar')
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 | trait | true | try |
var | while |
其实这一块对于java程序员可以不用说了,粗略讲一下,变量名以字母,$,或者_开头,但绝对不能是数字开头。
字符可以在如下的范围
然后后面的字符可以包含字母和数字
def name
def item3
def with_underscore
def $dollarStart
还有一个就是变量名不能是关键字
带引号的变量名一般是在点号运算符后面,例如person.name可以使用person."name"来进行表示,这样使得groovy 的变量命名变得很灵活
def map = [:]//定义一个map
map."an identifier with a space and double quotes" = "ALLOWED"//往map从存放键值对,相当于java的put
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"
另外的,也可以这么写,也就是说,只要是字符串都可以,下面的都是groovy 支持的字符串格式,作为变量名都是被支持的
map.'single quote'
map."double quote"
map.'''triple single quote'''
map."""triple double quote"""
map./slashy string/
map.$/dollar slashy string/$
普通的字符串与groovy 的GString(插值字符串),同样是可以被支持的,如下定义了一个firstname的变量,然后,在GString字符串中使用插值表达式进行引用
def firstname = "Homer"
map."Simpson-${firstname}" = "Homer Simpson"
assert map.'Simpson-Homer' == "Homer Simpson"
在groovy中,有2中字符串基础类型,一种是java中的java.lang.String
对象字符串,另外一种是groovy中支持的GStringsgroovy.lang.GString
,GStrings在其他语言中也被成为内插值字符串。
'a single-quoted string'
注意:单引号字符串是纯文本
java.lang.String
类型,不支持插值。
'a' + 'b'
'''a triple-single-quoted string'''
注意:单引号字符串是纯文本
java.lang.String
类型,不支持插值。
三重单引号字符串可能跨域多行,字符串内容可以跨界,无需分成几段,无需类似java中使用+进行拼接以及换行符号
def aMultilineString = '''line one
line two
line three'''
如果你的代码有缩进,可能会导致这个字符串达不到最终想要的效果,例如如下代码,左边有缩进,拿到的字符串肯定是有问题的。Groovy提供String#stripIndent()
和String#stripMargin()
来去除掉掉无用的数据。
def startingAndEndingWithANewline = '''
line one
line two
line three
'''
你估计可以看到,字符串第一个字符居然是换行符号,可以在后面使用 \ 将其去掉
def strippedFirstNewline = '''\
line one
line two
line three
'''
assert !strippedFirstNewline.startsWith('\n')
转义序列 | 字符 |
---|---|
\b | 退格键 |
\F | 换页 |
\n | 换行 |
\r | 回车 |
\s | 单空格 |
\t | 制表 |
\\ | 反斜杠 |
\’ | 单引号字符串中的单引号(对于三重单引号和双引号字符串是可选的) |
\" | 双引号字符串中的双引号(三重双引号和单引号字符串是可选的) |
没什么可说的,看代码
'你好的Unicode是: \u4F60\u597D'
"a double-quoted string"
除了单引号,与三引号单引号字符串外,任何Groovy 表达式都是支持插值的
def name = 'Guillaume' // a plain string
def greeting = "Hello ${name}"
插值表达式不仅仅支持变量引用,还支持数学运算呢
def sum = "The sum of 2 and 3 equals ${2 + 3}"
除了${}
占位符,我们还可以用$,而不需要花括号当然局限性就打了很多,只限在变量引用的范围。如下代码,
def name="UsErNaMeIs:CaT"
println "$name"
println "$name.toUpperCase()"//需要注意这个语句会抛出异常,因为解析器将其解释为"${name.toUpperCase}()",所以会抛出属性未找到异常。使用的时候建议谨慎使用
如下,如果表达式不明确,会出现.误引用的问题。所以一般建议是用${}
String bookName="红楼梦"
String desc="这本书的名字是:$bookName.作者是:xxx"
String desc2="这本书的名字是:${bookName}.作者是:xxx"
由于内插值其实相当一个返回特定数据的代码块,所以,其必也支持Groovy 的伟大特性之一闭包
def sParameterLessClosure = "1 + 2 == ${-> 3}" //不带参数的闭包
assert sParameterLessClosure == '1 + 2 == 3'
def sOneParamClosure = "1 + 2 == ${ w -> w << 3}" //使用<<3为w赋值,这里的<<3不是左移运算符哦
assert sOneParamClosure == '1 + 2 == 3'
内插值闭包提供了一个有趣的特点,惰性求值
def number = 1
def eagerGString = "value == ${number}"
def lazyGString = "value == ${ -> number }"
println eagerGString
println lazyGString
number=2
println eagerGString //输出还是1,字符串在编译的时候,就已经定型了
println lazyGString //输出的是2,也就是说,字符串的值只会在执行到的时候,才会去计算
如果有一个方法,其输入参数是java.lang.String
时候,但是我们传递了一groovy.lang.GString
的话,那么groovy 将会透明的帮我们调用toString()方法
//定义一个方法,要求输入的参数时java.lang.String类型
String takeString(String message) {
assert message instanceof String
return message
}
//定义一个GString类型的字符串
def message = "The message is ${'hello'}"
assert message instanceof GString //断言以证明其实GString类型
def result = takeString(message) //将GString 作为参数传递给taskString方法,发现其没有发生异常
assert result instanceof String
纯java字符串的hashCode是不可变的,而GString的字符串表示却有所不同,取决于其内插值,即使对于相同的GString和String ,其HashCode 也可能不同,如下例子,虽然都是一样的字符串,但是HashCode 是不同的
println "one:${1}".hashCode()
println "one:1".hashCode()
//但是,如果对GString进行toString()的话,就可以得到一样的哈希值
println "one:${1}".toString().hashCode()
println "one:1".hashCode()
由于GString的hash是不可预测的,所以,在使用map 存放key为String的数据的时候,应该尽量避免使用GString,因为对于map,其取数据时用hash值进行获取的,这一点是需要开发过程中特别注意的点。
def key = "a"
def m = ["${key}": "letter ${key}"]
assert m["a"] == null //可以断言发现是取不到值的
特点,多行,跟普通的字符串无差别
def name = 'Groovy'
def template = """
Dear Mr ${name},
You're the winner of the lottery!
Yours sincerly,
Dave
"""
//正常使用方法
def fooPattern = /.*foo.*/
///转义方法
def escapeSlash = /The character \/ is a forward slash/
//多行用法
def multilineSlashy = /one
two
three/
//补位方法
def color = 'blue'
def interpolatedSlashy = /a ${color} car/
//特例
assert '' == //
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 opening dollar slashy
$/$$ escaped closing dollar slashy
/$
char c1 = 'A' //使用char承接
assert c1 instanceof Character
def c2 = 'B' as char //强制转换
assert c2 instanceof Character
def c3 = (char)'C' //强转
assert c3 instanceof Character
// 基本类型
byte b = 1
char c = 2
short s = 3
int i = 4
long l = 5
//高精度
BigInteger bi = 6
//自动类型识别
def a = 1
assert a instanceof Integer
//如果是使用def 关键字进行变量的承接
// 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
使用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
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
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
float
double
java.lang.BigDecimal
//groovy支持e,与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
从上面的例子就可以看到有下划线的数字了。下划线仅仅为了方便人眼观看方便而为之
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
数字类型后缀
在java中,通常可以使用8l
类似l
的后缀的代码来表示Long类型数字,Groovy中也可以的
类型 | 后缀 |
---|---|
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') // 小写字母可能更有利与读写
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 | short | int | long | BigInteger | float | double | BigDecimal | |
---|---|---|---|---|---|---|---|---|---|
byte | int | int | int | int | long | BigInteger | double | double | BigDecimal |
char | int | int | int | long | BigInteger | double | double | BigDecimal | |
short | int | int | long | BigInteger | double | double | BigDecimal | ||
int | int | long | BigInteger | double | double | BigDecimal | |||
long | long | BigInteger | double | double | BigDecimal | ||||
BigInteger | BigInteger | double | double | BigDecimal | |||||
float | double | double | double | ||||||
double | double | double | |||||||
BigDecimal | BigDecimal |
// 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
def myBooleanVariable = true
boolean untypedBooleanVar = false
booleanField = true
def numbers = [1, 2, 3]
assert numbers instanceof List
assert numbers.size() == 3
//另外,也支持把不同类型的数据放置到一个列表中。
def heterogeneous = [1, "a", true]
//默认是采用ArrayList作为存储的容器的。
def arrayList = [1, 2, 3]
assert arrayList instanceof java.util.ArrayList
//但是也可以强转
def linkedList = [2, 3, 4] as LinkedList
assert linkedList instanceof java.util.LinkedList
//同样的也可以使用特定的容器进行存储
LinkedList otherLinked = [3, 4, 5]
assert otherLinked instanceof java.util.LinkedList
数组中的元素如何获取
def letters = ['a', 'b', 'c', 'd']
//直接下标获取法
assert letters[0] == 'a'
assert letters[1] == 'b'
//反向索引获取法
assert letters[-1] == 'd'
assert letters[-2] == 'c'
//给指定索引赋值
letters[2] = 'C'
assert letters[2] == 'C'
//往列表里面添加元素
letters << 'e'
assert letters[ 4] == 'e'
assert letters[-1] == 'e'
//获取指定索引的值
assert letters[1, 3] == ['b', 'd']
//获取指定索引范围的值
assert letters[2..4] == ['C', 'd', 'e']
String[] arrStr = ['Ananas', 'Banana', 'Kiwi'] //直接使用容器承接
assert arrStr instanceof String[]
assert !(arrStr instanceof List)
def numArr = [1, 2, 3] as int[] //直接使用as 声明
assert numArr instanceof int[]
assert numArr.size() == 3
//因为{}花括号已经被闭包所占用了,所以此处使用的是数组进行包括起来进行map的初始化
def colors = [red: '#FF0000', green: '#00FF00', blue: '#0000FF']
//获取map中元素的方法
assert colors['red'] == '#FF0000'
assert colors.green == '#00FF00'
//设置map中对应key value
colors['pink'] = '#FF00FF'
colors.yellow = '#FFFF00'
assert colors.pink == '#FF00FF'
assert colors['yellow'] == '#FFFF00'
//默认是采用LinkedHashMap类型的方式进行初始化
assert colors instanceof java.util.LinkedHashMap
//如果尝试获取一个不存在的key,将会得到一个null
assert colors.unknown == null
def key = 'name'
def person = [key: 'Guillaume']
assert !person.containsKey('name') //使用name进行获取是得不到的
assert person.containsKey('key') //只能使用key进行获取,所以由此可以看出map的key不支持变量方式
//那么如果我们想要使用变量作为map 的key的时候,应该怎么做?只需要在变量名左右加上括号即可
person = [(key): 'Guillaume']
1.2.1 数学运算符
assert +3 == 3
assert -4 == 0 - 4
assert -(-1) == 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
def f = 3
f **= 2
assert f == 9
Operator | Purpose |
---|---|
== | equal |
!= | 不同 |
< | 小于 |
<= | 小于等于 |
> | 大于 |
>= | 大于等于 |
=== | 强等于(Since Groovy 3.0.0) |
!== | 强不等于 |
===
跟!==
都是通过调用对象的is()方法来进行甄别的
import groovy.transform.EqualsAndHashCode
@EqualsAndHashCode
class Creature { String type }
def cat = new Creature(type: 'cat')
def copyCat = cat
def lion = new Creature(type: 'cat')
assert cat.equals(lion) // java自带的判别方法
assert cat == lion // Grovyy短判别方法
assert cat.is(copyCat) // Groovy视为相等
assert cat === copyCat // 两者是相等的
assert cat !== lion // 两者是不相等的
&&
: logical “and”||
: logical “or”!
: logical “not”&
: bitwise “and”|
: bitwise “or”^
: bitwise “xor” (exclusive “or”)~
: bitwise negationassert (!true) == false
assert (!'foo') == false
assert (!'') == true
result = (string!=null && string.length()>0) ? 'Found' : 'Not found'
displayName = user.name ? user.name : 'Anonymous'
displayName = user.name ?: 'Anonymous' //上面的例子等价于下面
import groovy.transform.ToString
@ToString
class Element {
String name
int atomicNumber
}
def he = new Element(name: 'Helium')
he.with {
name = name ?: 'Hydrogen' //这个形式可以使用下面的形式进行替换
atomicNumber ?= 2 // 不存在的时候进行赋值,存在则不进行赋值,效果与上面的一样
}
assert he.toString() == 'Element(Helium, 2)'
java中,我们场景因为你对象是空的导致的调用方法引发NPE,所以在使用之前经常要判断对象是否为null,groovy中可以使用?.
来进行对象属性的的调用,即使属性为null也不至于报错。
def person = Person.find { it.id == 123 }
def name = person?.name
assert name == null
class User {
public final String name
User(String name) { this.name = name}
String getName() { "Name: $name" }
}
def user = new User('Bob')
assert user.name == 'Name: Bob' //看似获取的只是属性值,其实Groovy已经偷偷的帮我们调用了getName()方法
//那么如果我们不希望Groovy 帮我们自动调用方法的时候,应该怎么办呢?答案是通过@进行调用即可
assert user.@name == 'Bob' //.@强制使用field而非getter
.&
可以用于把方法存储于变量之中
def str = 'example of method reference'
def fun = str.&toUpperCase //得到str对象的toUpperCase方法的引用
def upper = fun() //调用方法,这里有点神奇哈。直接调用方法而无需送入对象,是不是对象与方法已经绑定了呢?
assert upper == str.toUpperCase()
方法指针存储者诸多优点,第一方法指针的存储类型实际上是groovy.lang.Closure
,所以他可以在任何一个使用闭包的地方进行使用
//transform方法对list中每一个元素都进行action方法的调用,并且将转化后的类型以列表的方式返回
def transform(List elements, Closure action) {
def result = []
elements.each {
result << action(it)
}
result
}
//我们定义一个用于吧person对象转成字符串的方法
String describe(Person p) {
"$p.name is $p.age"
}
//取得方法的引用
def action = this.&describe
def list = [
new Person(name: 'Bob', age: 42),
new Person(name: 'Julia', age: 35)]
//使用
assert transform(list, action) == ['Bob is 42', 'Julia is 35']
//使用.&new可得到一个对象的构造函数
def foo = BigInteger.&new
def fortyTwo = foo('42')
assert fortyTwo == 42G
//可以得到class的方法引用,但使用的时候,第一个参数要被调用的对象传入,可以视为this对象
def instanceMethod = String.&toUpperCase
assert instanceMethod('foo') == 'FOO'
groovy同样支持java8提供的::运算符
import groovy.transform.CompileStatic
import static java.util.stream.Collectors.toList
@CompileStatic
void methodRefs() {
assert 6G == [1G, 2G, 3G].stream().reduce(0G, BigInteger::add)
assert [4G, 5G, 6G] == [1G, 2G, 3G].stream().map(3G::add).collect(toList())
assert [1G, 2G, 3G] == [1L, 2L, 3L].stream().map(BigInteger::valueOf).collect(toList())
assert [1G, 2G, 3G] == [1L, 2L, 3L].stream().map(3G::valueOf).collect(toList())
}
1.2.7 正则运算符
def p = ~/foo/
assert p instanceof Pattern//得到的是一个Pattern实例对象
p = ~'foo'
p = ~"foo"
p = ~$/dollar/slashy $ string/$
p = ~"${pattern}"
//可以使用功能=~来进行正则的匹配
def text = "some text to match"
def m = text =~ /match/
assert m instanceof Matcher
if (!m) {
throw new RuntimeException("Oops, text not found!")
}
//使用==~来得到一个是否匹配的结果
m = text ==~ /match/
assert m instanceof Boolean
if (m) {
throw new RuntimeException("Should not reach that point!")
}
class Car {
String make
String model
}
def cars = [
new Car(make: 'Peugeot', model: '508'),
new Car(make: 'Renault', model: 'Clio')]
def makes = cars*.make
//cars.stream().map({it.make}).collect(Collectors.toList())
assert makes == ['Peugeot', 'Renault']
//
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]//可见*.的原理为对象必须是继承自Iterable,可迭代的。
assert composite*.name == ['Foo','Bar']
//#########################################################################
class Make {
String name
List<Model> models
}
//支持多级调用
@Canonical
class Model {
String name
}
def cars = [
new Make(name: 'Peugeot',
models: [new Model('408'), new Model('508')]),
new Make(name: 'Renault',
models: [new Model('Clio'), new Model('Captur')])
]
def makes = cars*.name
assert makes == ['Peugeot', 'Renault']
def models = cars*.models*.name//多级调用
assert models == [['408', '508'], ['Clio', 'Captur']]
assert models.sum() == ['408', '508', 'Clio', 'Captur'] // flatten one level
assert models.flatten() == ['408', '508', 'Clio', 'Captur'] // flatten all levels (one in this case)
//#########################################################################
class Car {
String make
String model
}
def cars = [
[
new Car(make: 'Peugeot', model: '408'),
new Car(make: 'Peugeot', model: '508')
], [
new Car(make: 'Renault', model: 'Clio'),
new Car(make: 'Renault', model: 'Captur')
]
]
def models = cars.collectNested{ it.model }//collectNested语法为*.的简写方式
assert models == [['408', '508'], ['Clio', 'Captur']]
int function(int x, int y, int z) {
x*y+z
}
def args = [4,5,6]
assert function(*args) == 26//*有类似于python的解构的功能,把数组中对象按照顺序作为方法的参数传入
def items = [4,5]
def list = [1,2,3,*items,6] //可以直接用来解构列表
assert list == [1,2,3,4,5,6]
def m1 = [c:3, d:4]
def map = [a:1, b:2, *:m1] //直接将m1解构,并且存入map中
assert map == [a:1, b:2, c:3, d:4]
def m1 = [c:3, d:4] //解构的时候,遇到同样key的值,以顺序前后进行放置,后者覆盖前者
def map = [a:1, b:2, *:m1, d: 8]
assert map == [a:1, b:2, c:3, d:8]
//类似于python ,但是又不需要多余的两个点号。
def range = 0..5
assert (0..5).collect() == [0, 1, 2, 3, 4, 5] //给定范围,含头,含尾
assert (0..<5).collect() == [0, 1, 2, 3, 4] //给定范围,含头不含尾
assert (0..5) instanceof List //默认初始类型为List实际为(groovy.lang.Range)
assert (0..5).size() == 6
Range的实现是非常轻量级,因为它只存储一个上界与一个下界,可以从一个实现了Comparable
接口的对象,并且实现next()
跟previous()
方法,以得到上一个或者下一个元素的实际值,例如,可以创建一个字符Range
assert ('a'..'d').collect() == ['a','b','c','d']
<=>
是compareTo的委托方法
assert (1 <=> 1) == 0 //1==1 返回0
assert (1 <=> 2) == -1 //1<2 返回-1
assert (2 <=> 1) == 1 //2>1 返回1
assert ('a' <=> 'z') == -1 //'a'<'z'
下标运算符是getAt 跟putAt方法的缩写
def list = [0,1,2,3,4]
assert list[2] == 2
list[2] = 4
assert list[0..2] == [0,1,4]
list[0..2] = [6,6,6]
assert list == [6,6,6,3,4]
可以定义一个类,该类实现getAt与putAt方法,则可以通过对象[index]下标对元素进行获取
class User {
Long id
String name
def getAt(int i) {
switch (i) {
case 0: return id
case 1: return name
}
throw new IllegalArgumentException("No such element $i")
}
void putAt(int i, def value) {
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')
assert user[0] == 1
assert user[1] == 'Alex'
user[1] = 'Bob'
assert user.name == 'Bob'
同样的,所给下标如果无法获取到元素,也就是对象是null,就会导致异常的出现,提供了下面
类似于?.同样的也有?[],可以避免异常
String[] array = ['a', 'b']
assert 'b' == array?[1] // 获取方法
array?[1] = 'c' // 设置方法
assert 'c' == array?[1]
array = null
assert null == array?[1] // 返回null
array?[1] = 'c' // 放弃设置值
assert null == array?[1]
def personInfo = [name: 'Daniel.Sun', location: 'Shanghai']
assert 'Daniel.Sun' == personInfo?['name'] // 获取map值
personInfo?['name'] = 'sunlan' // 设置map值
assert 'sunlan' == personInfo?['name']
personInfo = null
assert null == personInfo?['name'] // 返回null
personInfo?['name'] = 'sunlan' // 忽略错误
assert null == personInfo?['name']
def list = ['Grace','Rob','Emmy']
assert ('Emmy' in list)
//等同于调用 `list.contains('Emmy')` 或者 `list.isCase('Emmy')`
def list1 = ['Groovy 1.8','Groovy 2.0','Groovy 2.3']
def list2 = ['Groovy 1.8','Groovy 2.0','Groovy 2.3']
assert list1 == list2 //仅仅判断数值,返回true
assert !list1.is(list2) //判断引用,故而返回false
Integer x = 123
String s = (String) x //强转本质也可以看做调用当前对象的asType方法
//可以使用如下方法,来吧对象转化成为一个完全与之无任何关系的对象
class Identifiable {
String name
}
class User {
Long id
String name
def asType(Class target) {
if (target == Identifiable) {
return new Identifiable(name: name)
}
throw new ClassCastException("User cannot be coerced into $target")
}
}
def u = new User(name: 'Xavier')
def p = u as Identifiable //强转为异父异母的对象
assert p instanceof Identifiable //强转后的对象成为了 Identifiable
assert !(p instanceof User) //并且已经不再是User。因为它在asType方法new出来的
List<String> strings = new LinkedList<>()
//在groovy 中,<>完全无用
class MyCallable {
int call(int x) {
2*x
}
}
def mc = new MyCallable()
assert mc.call(2) == 4 //直接调用call方法
assert mc(2) == 4 //通过运算符调用对象的call方法,默认就是调用该方法
Level | Operator(s) | Name(s) |
---|---|---|
1 | new () | object creation, explicit parentheses |
() {} [] | method call, closure, literal list/map | |
. .& .@ | member access, method closure, field/attribute access | |
?. * *. *: | safe dereferencing, spread, spread-dot, spread-map | |
~ ! (type) | bitwise negate/pattern, not, typecast | |
[] ?[] ++ -- | list/map/array (safe) index, post inc/decrement | |
2 | ** | power |
3 | ++ -- + - | pre inc/decrement, unary plus, unary minus |
4 | * / % | multiply, div, remainder |
5 | + - | addition, subtraction |
6 | << >> >>> .. ..< | left/right (unsigned) shift, inclusive/exclusive range |
7 | < <= > >= in !in instanceof !instanceof as | less/greater than/or equal, in, not in, instanceof, not instanceof, type coercion |
8 | == != <=> === !== | equals, not equals, compare to, identical to, not identical to |
=~ ==~ | regex find, regex match | |
9 | & | binary/bitwise and |
10 | ^ | binary/bitwise xor |
11 | | | binary/bitwise or |
12 | && | logical and |
13 | || | logical or |
14 | ? : | ternary conditional |
?: | elvis operator | |
15 | = **= *= /= %= += -= <<= >>= >>>= &= ^= |= ?= | various assignments |
class Bucket {
int size
Bucket(int size) { this.size = size }
//此方法为运算符重载
Bucket plus(Bucket other) {
return new Bucket(this.size + other.size)
}
}
def b1 = new Bucket(4)
def b2 = new Bucket(11)
assert (b1 + b2).size == 15 //调用对象的plus方法
如下表为运算符重载表
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 in b | b.isCase(a) |
/ | a.div(b) | << | a.leftShift(b) |
% | a.mod(b) | >> | a.rightShift(b) |
** | a.power(b) | >>> | a.rightShiftUnsigned(b) |
| | a.or(b) | ++ | a.next() |
& | a.and(b) | -- | a.previous() |
^ | a.xor(b) | +a | a.positive() |
as | a.asType(b) | -a | a.negative() |
a() | a.call() | ~a | a.bitwiseNegate() |
//跟java 是一样的
package com.yoursite
// importing the class MarkupBuilder
import groovy.xml.MarkupBuilder
// using the imported class to create an object
def xml = new MarkupBuilder()
assert xml != null
以下的包均为默认导入的包
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
// importing the class MarkupBuilder
import groovy.xml.MarkupBuilder
// using the imported class to create an object
def xml = new MarkupBuilder()
assert xml != null
import groovy.xml.*
def markupBuilder = new MarkupBuilder()
assert markupBuilder != null
assert new StreamingMarkupBuilder() != null
import static Boolean.FALSE
assert !FALSE //use directly, without Boolean prefix!
import static Calendar.getInstance as now//使用as给导入的类或者方法起别名
assert now().class == Calendar.getInstance().class
import static java.lang.Math.*
assert sin(0) == 0.0
assert cos(0) == 1.0
import java.util.Date
import java.sql.Date as SQLDate
Date utilDate = new Date(1000L)
SQLDate sqlDate = new SQLDate(1000L)
assert utilDate instanceof java.util.Date
assert sqlDate instanceof java.sql.Date
使用类方式实现Groovy world,需要定义一个类,并且让该类实现static void main(…)方法
class Main {
static void main(String... args) {
println 'Groovy world!'
}
}
但是使用groovy 脚本方式,则无需如此
println 'Groovy world!'
一个groovy 脚本一般是编译成一个类(Script),并且代码被拷贝到该类所实现的run方法,代码如下
import org.codehaus.groovy.runtime.InvokerHelper
class Main extends Script {
def run() {
println 'Groovy world!'
}
static void main(String[] args) {
InvokerHelper.runScript(Main, args)
}
}
int fib(int n) {
n < 2 ? 1 : fib(n-1) + fib(n-2)
}
assert fib(10)==89
//另外在字符串中也可以直接调用方法
println 'Hello'
int power(int n) { 2**n }
println "2^6==${power(6)}"
//上面的代码最终将会转化为如下的代码
import org.codehaus.groovy.runtime.InvokerHelper
class Main extends Script {
int power(int n) { 2** n} //power方法称为Main类下的以一个成员方法
def run() {
println 'Hello'
println "2^6==${power(6)}"
}
static void main(String[] args) {
InvokerHelper.runScript(Main, args)
}
}
//使用java方式进行变量定义
int x = 1
int y = 2
assert x+y == 3
//脚本中的变量无需声明类型
x = 1
y = 2
assert x+y == 3
//需要注意这两种定义方式是有区别的。
//使用声明类型的方式的变量,在其他方法内部是不可见的,但是在下面的方式定义的变量是全局可见的。
Groovy支持的基本数据类型与java无异
byte
(8 bit), short
(16 bit), int
(32 bit) 与 long
(64 bit)float
(32 bit) 跟 double
(64 bit)boolean
类型 (要么 true
或 false
)char
类型 (16 bit, 等同于数字类型, 可以视为 UTF-16 编码,因为一共占用16为4个字节)Groovy 同样适用java的包装类型
Primitive type | 包装类型 |
---|---|
boolean | Boolean |
char | Character |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
class Foo {
static int i
}
assert Foo.class.getDeclaredField('i').type == int.class
assert Foo.i.class != int.class && Foo.i.class == Integer.class//实际上Groovy 是把变量i作为Integer进行存储的,而非int,
Groovy 中定义的类与Java的类基本上是一样的。但是还是有稍微的差异的 。
class Person {
String name
Integer age
def increaseAge(Integer years) {
this.age += years
}
}
def p = new Person()
class Outer {
private String privateStr
def callInnerMethod() {
new Inner().methodA()
}
class Inner {
def methodA() {
println "${privateStr}."
}
}
}
abstract class Abstract {
String name
abstract def abstractMethod()
def concreteMethod() {
println 'concrete'
}
}
interface Greeter {
void greet(String name)
}
Groovy提供3种方式调用构造函数
class PersonConstructor {
String name
Integer age
PersonConstructor(name, age) {
this.name = name
this.age = age
}
}
//方式1 ,按照参数定义位置
def person1 = new PersonConstructor('Marie', 1)
def person2 = ['Marie', 2] as PersonConstructor //使用as强转
PersonConstructor person3 = ['Marie', 3] //使用对象承接
如果类没有声明默认的构造函数,可以通过以map方式作为参数,送入构造函数进行初始化
class PersonWOConstructor {
String name
Integer age
}
def person4 = new PersonWOConstructor()
def person5 = new PersonWOConstructor(name: 'Marie')
def person6 = new PersonWOConstructor(age: 1)
def person7 = new PersonWOConstructor(name: 'Marie', age: 2)
混合的情况
def foo(Map args, Integer number) { "${args.name}: ${args.age}, and the number is ${number}" }
foo(name: 'Marie', age: 1, 23)
foo(23, name: 'Marie', age: 1) //错误方法
def foo(String par1, Integer par2 = 1) { [name: par1, age: par2] }//par2在没有传递的时候默认为1
assert foo('Marie').age == 1
def foo(Object... args) { args.length }
assert foo() == 0
assert foo(1) == 1
assert foo(1, 2) == 2
def badRead() {
new File('doesNotExist.txt').text
}
//在方法的参数中声明异常
shouldFail(FileNotFoundException) {
badRead()
}
字段(Field)是类的成员
public
protected
private
static
, final
, synchronized
class Data {
private int id
protected String description
public static final boolean DEBUG = false
}
属性(properties)
属性是一个对外可见的特征
class Person {
String name
int age
}
//Groovy会为属性自动加上setter跟getter
//但是如果属性一旦被final修饰了,则不会自动创建setter跟getter
class Person {
final String name
final int age
Person(String name, int age) {
this.name = name
this.age = age
}
}
class Person {
String name
void name(String name) {
this.name = "Wonder$name"
}
String wonder() {
this.name
}
}
def p = new Person()
p.name = 'Marge' //不会调用setter
assert p.name == 'Marge'
p.name('Marge') //自动调用setter
assert p.wonder() == 'WonderMarge'
//如果我想得到一个对象所有的属性(properites)怎么办?
class Person {
String name
int age
}
def p = new Person()
assert p.properties.keySet().containsAll(['name','age'])//直接调用对象的内置属性properites就可以得到全部定义的属性
class PseudoProperties {
// 一个伪属性 "name"
void setName(String name) {}
String getName() {}
// 一个伪只读属性 "age"
int getAge() { 42 }
//一个伪的只写属性 "groovy"
void setGroovy(boolean groovy) { }
}
def p = new PseudoProperties()
p.name = 'Foo'
assert p.age == 42
p.groovy = true