Groovysh 相关配置
Variables
Shell 变量是无类型的,即不是 def
类型也不是任何其它类型
下面定义了一个 Shell 变量
foo = "bar"
但是下面一行定义了一个本地变量,并且不会保存到 shell 环境中
def foo = "bar"
可以通过设置 :set interpreterMode
来定义有类型的变量(def
或其它类型)
字符串插值(String interpolation)
除了单引号和三重单引号的字符串外,任何 Groovy 表达式都可以插入到字符串中。插值用表达式得到的值替换字符串中的占位符。占位符表达式由 ${}
包围。对于明确的点分表达式(dotted expression),可以省略花括号,即只使用 $
前缀。
def name = 'Guillaume' // a plain string
def greeting = "Hello ${name}"
assert greeting.toString() == 'Hello Guillaume'
在 ${}
占位符中不但允许使用表达式(expression),还可以使用语句(statements)。但是,一条语句的值是 null
。因此,如果在该占位符中插入了多个语句,则最后一个应以某种方式返回要插入的有意义的值。例如,”1 + 2的和等于 ${def a = 1; def b = 2; a + b}
“,但是最好在 GString 占位符中只使用简单的表达式。
除了 ${}
占位符,我们还可以在点分表达式(dotted expression)前使用单独的 $
符号:
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 认为你正在尝试访问 number
对象的 toString
属性,而不是 toString()
方法:
shouldFail(MissingPropertyException) {
println "$number.toString()"
}
你可以认为 "$number.toString()"
会被解释为 "${number.toString}()"
。
Optionality
- 在不产生歧义并且至少有一个参数的情况下,方法调用可以省略括号
()
println 'Hello World'
def maximum = Math.max 5, 10
如果方法没有参数或会产生歧义,则必须带括号
println()
println(Math.max(5, 10))
- 如果一行只有一个语句,可以省略行尾的分号
;
,多个语句在一行则需要使用分号来分隔
boolean a = true; assert a
-
方法或闭包的最后一个表达式的计算结果会作为返回值返回,因此关键字
return
可以省略 -
Groovy 中类和方法默认是
public
,因此下面两段定义一样
public class Server {
public String toString() { "a server" }
}
class Server {
String toString() { "a server" }
}
Groovy中的真值(The Groovy Truth)
// 布尔值 true
assert true
assert !false
// 非空集合
assert [1, 2, 3]
assert ![]
// 至少一个匹配
assert ('a' =~ /a/)
assert !('a' =~ /b/)
// iterator 和 enumeration 可以读取下一个元素
assert [0].iterator()
assert ![].iterator()
Vector v = [0] as Vector
Enumeration enumeration = v.elements()
assert enumeration
enumeration.nextElement()
assert !enumeration
// 非空映射
assert ['one' : 1]
assert ![:]
// 非空字符串
assert 'a'
assert !''
// 非零值
assert 1
assert -1
assert !0
//非空引用
assert new Object()
assert !null
闭包(Closure)
闭包定义
{ [closureParameters -> ] statements }
如果函数最后一个参数是闭包,可以将闭包放在参数列表外面,附在函数调用之后
// 定义一个有两个参数的函数,最后一个参数是闭包
def f (n, cl) {
cl(n)
}
// 定义一个闭包
def isEven = {it % 2 == 0}
// 以下函数调用等价
f(6, isEven)
// 参数可以不带括号
f 6, isEven
// 传入一个匿名闭包
f(6, {it % 2 != 0})
// 最后一个参数是闭包的话,可以写在参数括号外面
f(6) {
it % 2 != 0
}
// 不带参数括号,传入匿名闭包,注意参数间的逗号
f 6, {
it % 2 == 0
}
读取文件
new File(baseDir, 'haiku.txt').eachLine { line ->
println line
}
eachLine
方法按行遍历对象,其闭包参数可以接受一或两个参数,第2个参数表示行数。
new File(baseDir, 'haiku.txt').eachLine { line, nb ->
println "Line $nb: $line"
}
将文本中的每一行放入一个列表
def list = new File(baseDir, 'haiku.txt').collect {it}
// 或
def list = new File(baseDir, 'haiku.txt').readLines()
执行外部命令(Executing External Processes)
将要执行的外部命令作为字符串,然后调用 execute
方法,该方法返回一个 java.lang.Process
对象,允许处理 in/out/err
字节流以及 Exit Code
。
def process = "ls -l".execute()
println "Found text ${process.text}"
将多行的字符串读入列表
String str = 'ls -l'.execute().text
List list = str.readLines()
使用集合(Working with Collections)
列表
[]
操作符用来创建列表
def list = [5, 6, 7, 8]
assert list.get(2) == 7
assert list[2] == 7
assert list instanceof java.util.List
def emptyList = []
assert emptyList.size() == 0
emptyList.add(5)
assert emptyList.size() == 1
使用 each
和 eachWithIndex
方法遍历列表,它们接受一个闭包作为参数,并在每个列表元素上执行闭包代码
[1, 2, 3].each {
println "Item: $it" // `it` is an implicit parameter corresponding to the current element
}
['a', 'b', 'c'].eachWithIndex { it, i -> // `it` is the current element, while `i` is the index
println "$i: $it"
}
类似于 python 的 map 方法,集合可以使用 collect
方法对集合元素进行转换,它接收一个闭包作为参数,并在所有元素上执行该闭包,返回一个转换后的新列表。如果同时传入一个列表和闭包作为参数,则转换后的元素会追加到该列表。
assert [1, 2, 3].collect { it * 2 } == [2, 4, 6]
// shortcut syntax instead of collect
assert [1, 2, 3]*.multiply(2) == [1, 2, 3].collect { it.multiply(2) }
def list = [0]
// it is possible to give `collect` the list which collects the elements
assert [1, 2, 3].collect(list) { it * 2 } == [0, 2, 4, 6]
assert list == [0, 2, 4, 6]
inject(Object initialValue, Closure closure)
遍历集合,将初始值和集合第一个元素作为参数传递给闭包,闭包的返回值和集合的第二个元素再次作为参数传递给闭包,一直重复直到集合中元素遍历完成。
assert 'The quick brown fox' ==
['quick', 'brown', 'fox'].inject('The') { acc, val -> acc << " ${val}" }
def max = { a, b -> [a, b].max() } // define a closure
def animals = ['bat', 'rat', 'cat']
assert 'rat' == animals.inject('aaa', max)
上例的直观解释
initVal animals[0]
v v
max('aaa', 'bat') => 'bat' animals[1]
v v
max('bat', 'rat') => 'rat' animals[2]
v v
max('rat', 'cat') => 'rat'
inject(Closure closure)
方法使用集合中第一个元素作为初始值。
assert [1, 2, 3].find { it > 1 } == 2 // find 1st element matching criteria
assert [1, 2, 3].findAll { it > 1 } == [2, 3] // find all elements matching critieria
assert [1, 2, 3].every { it < 5 } // returns true if all elements match the predicate
assert [1, 2, 3].any { it > 2 } // returns true if any element matches the predicate
assert [1, 2, 3, 4, 5, 6].sum() == 21 // sum anything with a plus() method
assert [1, 2, 3].sum(1000) == 1006
assert [1, 2, 3].join('-') == '1-2-3' // String joining
assert [1, 2, 3].inject(0) {
count, item -> count + item // reduce operation
} == 6
assert 'a' in ['a','b','c'] // returns true if an element belongs to the list
assert ['a','b','c'].contains('a') // equivalent to the `contains` method in Java
assert [1,3,4].containsAll([1,4]) // `containsAll` will check that all elements are found
assert [1,2,3,3,3,3,4,5].count(3) == 4 // count the number of elements which have some value
assert [1,2,3,3,3,3,4,5].count { it%2==0 } == 2 // count the number of elements which match the predicate
assert [1,2,4,6,8,10,12].intersect([1,3,6,9,12]) == [1,6,12]
assert [1,2,3].disjoint( [4,6,9] )
assert ![1,2,3].disjoint( [2,4,6] )
添加删除列表元素(Adding or removing elements)
使用 []
创建空列表,<<
向列表追加元素
def list = []
list << 5
assert list.size() == 1
list << 7 << 'i' << 11
assert list == [5, 7, 'i', 11]
list << ['m', 'o']
assert list == [5, 7, 'i', 11, ['m', 'o']]
//first item in chain of << is target list
assert ([1, 2] << 3 << [4, 5] << 6) == [1, 2, 3, [4, 5], 6]
//using leftShift is equivalent to using <<
assert ([1, 2, 3] << 4) == ([1, 2, 3].leftShift(4))
assert [1, 2] + 3 + [4, 5] + 6 == [1, 2, 3, 4, 5, 6]
// equivalent to calling the `plus` method
assert [1, 2].plus(3).plus([4, 5]).plus(6) == [1, 2, 3, 4, 5, 6]
注意: 与 <<
相比,每次调用 +
操作符都会创建一个新列表,可能导致性能问题。
使用 remove
方法来删除元素,可以传入元素索引,也可以传入元素值。如果列表元素是整数值或任何其它类型,可以使用对应的 removeAt
和 removeElement
方法
def list = ['a','b','c','d','b']
assert list.remove(2) == 'c' // remove the third element, and return it
assert list == ['a','b','d','b']
assert list.remove('b') // remove first 'b', and return true because element removed
assert ! list.remove('z') // return false because no elements removed
assert list == ['a','d','b']
def list = [1,2,3,4,5,4]
assert list.removeAt(1) == 2 // remove element at index 1, and return it
assert list == [1,3,4,5,4]
assert list.removeElement(4) // remove first 5 and return true
assert list == [1,3,5,4]
使用 clear
方法清空列表
def list= ['a',2,'c',4]
list.clear()
assert list == []
映射(Maps)
[:]
操作符用来创建映射
def emptyMap = [:]
assert emptyMap.size() == 0
emptyMap.put("foo", 5)
def map = [name: 'Gromit', likes: 'cheese', id: 1234]
assert map.name == 'Gromit'
assert map.get('likes') == 'cheese'i
assert map['id'] == 1234
assert map instanceof java.util.Map
注意: 映射的键默认是 String 类型,也就是说 [a:1]
与 ['a':1]
一样,如果有个变量叫 a
,我们又想用该变量的值作为映射的键,需要使用括号 ()
来转义。
def a = 'Bob'
ages = [(a): 43] // now we escape `a` by using parenthesis
assert ages['Bob'] == 43 // and the value is found!
def map = [
Bob : 42,
Alice: 54,
Max : 33
]
// `entry` is a map entry
map.each { entry ->
println "Name: $entry.key Age: $entry.value"
}
// `entry` is a map entry, `i` the index in the map
map.eachWithIndex { entry, i ->
println "$i - Name: $entry.key Age: $entry.value"
}
// Alternatively you can use key and value directly
map.each { key, value ->
println "Name: $key Age: $value"
}
// Key, value and i as the index in the map
map.eachWithIndex { key, value, i ->
println "$i - Name: $key Age: $value"
}
添加删除映射元素(Adding or removing elements)
使用 put
方法添加键值对,下标操作符 []
或 putAll
方法添加或修改元素
def defaults = [1: 'a', 2: 'b']
def overrides = [2: 'z', 5: 'x', 13: 'x']
def result = new LinkedHashMap(defaults)
result.put(15, 't')
result[17] = 'u'
result.putAll(overrides)
assert result == [1: 'a', 2: 'z', 5: 'x', 13: 'x', 15: 't', 17: 'u']
使用 clear
方法清空映射
def m = [1:'a', 2:'b']
m.clear()
assert m == [:]
使用 groupBy
方法对映射进行分类
assert ['a', 7, 'b', [2, 3]].groupBy {
it.class
} == [(String) : ['a', 'b'],
(Integer) : [7],
(ArrayList): [[2, 3]]
]
assert [
[name: 'Clark', city: 'London'], [name: 'Sharma', city: 'London'],
[name: 'Maradona', city: 'LA'], [name: 'Zhang', city: 'HK'],
[name: 'Ali', city: 'HK'], [name: 'Liu', city: 'HK'],
].groupBy { it.city } == [
London: [[name: 'Clark', city: 'London'],
[name: 'Sharma', city: 'London']],
LA : [[name: 'Maradona', city: 'LA']],
HK : [[name: 'Zhang', city: 'HK'],
[name: 'Ali', city: 'HK'],
[name: 'Liu', city: 'HK']],
]
范围(Range)
..
(inclusive, 闭区间)..<
(half-open, 左闭右开区间)
// an inclusive range
def range = 5..8
assert range.size() == 4
assert range.get(2) == 7
assert range[2] == 7
assert range instanceof java.util.List
assert range.contains(8)
// lets use a half-open range
range = 5..<8
assert range.size() == 3
assert range.get(2) == 7
assert range[2] == 7
assert range instanceof java.util.List
assert !range.contains(8)
//get the end points of the range without using indexes
range = 1..10
assert range.from == 1
assert range.to == 10
范围可用于所有实现了 java.lang.Comparable
接口的 Java 对象,比如 String
对象
// an inclusive range
def range = 'a'..'d'
assert range.size() == 4
assert range.contains('a')
assert range.contains('d')
切片(Slicing)
可以使用 subscript expression
对 List
, Array
, Map
和 String
对象进行索引。
def text = 'nice cheese gromit!'
def x = text[2]
assert x == 'c'
assert x.class == String
def sub = text[5..10]
assert sub == 'cheese'