安装以及简单使用
安装
到Groovy官网下载安装包。(本人使用的是windows,故以windows为例)
- 按照安装程序指引操作。
- 设置GROOVY_HOME环境变量和Groovy路径。GROOVY_HOME设置为安装目录(C:\Groovy\Groovy-2.4.7),Path中添加%GROOVY_HOME%/bin。
- 在命令行中运行groovy -v,验证是否安装正确。
使用groovysh
在命令行窗口输入groovysh
,我们会看到一个shell。然后就可以输入一些groovy代码了。
groovy:000> Math.sqrt(16)
===> 4.0
groovy:000>
输入:exit
退出shell。
使用groovyConsole
windows用户可以在安装目录的bin中找到groovyConsole.bat,双击,控制台GUI工具就会弹出。可以在GUI上进行编码操作。
命令行中运行Groovy文件
- 编写一个groovy文件。
运行groovy命令执行。
hello.groovy内容: println "Hello Groovy!" 运行命令: groovy hello 结果:Hello Groovy!
Groovy基础
Groovy支持Java语法,并且保留了Java语义,我们可以随心所欲地混用两种语言。
一些特性:
- return方法几乎总是可选的
- 可以不使用分号分隔语句
- 方法和类默认是public的
- 静态方法内可以使用this来引用Class对象。
- Groovy的==等价于Java的equals()。如果实现了Comparable接口,那么==就相当于compareTo()方法
- def、in、it不建议作为变量的命名。def用于定义方法、属性和局部变量;in用于在for循环中指定循环期间。
- Groovy闭包用花括号定义,匿名类也用花括号定义
http://groovy-lang.org/differences.html列出来Groovy和Java的不同。
实现循环的方式
for循环 & range语法
for (int i = 0; i < 3; i++){
println("this is " + i);
}
for(i in 0..2){
println("this is " + i);
}
upto()
0.upto(2){
println("this is $it");
}
0是Integer的一个实例。$it代表进行循环时的索引值。upto()方法接受一个闭包作为参数。
times()
3.times {
println("this is $it");
}
范围从0开始。
step()
0.step(10,2){
println("this is $it");
}
表示,从0到10,每次加2。
安全导航操作符 - ?.
安全导航(safe-navigation)操作符(?.)在安全的情况下才会去调用指定的方法或者属性。
def foo(str){
// if(str !=null ) str.reverse();
str?.reverse();
}
println(foo('evil'))
println(foo(null))
异常处理
对于那些我们不想处理的异常,或者不适合在代码的当前处理的异常,Groovy并不强制我们处理。我们不处理的任何异常都会被自动传递给更高一层。所以,我们可以在更高层次的任何地方处理我们想要处理的异常。
JavaBean
class Car {
def miles = 0;
final year;
Car(theYear){
year = theYear;
}
}
Car car = new Car(2016);
println "Year: $car.year";
println "Miles: $car.miles";
println "Setting miles: ";
car.miles = 25;
println "Miles: $car.miles";
- def声明一个属性。
- Groovy会为JavaBean创建一个访问器(getter)和一个更改器(setter)。
- 使用final来声明属性时,该属性为只读。修改final字段会导致异常。
- Groovy的实现不区分public、private和protected。
- 要把变量设置为私有的,必须实现一个拒绝任何修改的更改器(setter)。
示例如下:
class Car {
private miles = 0;
final year;
Car(theYear){
year = theYear;
}
def getMiles(){
miles;
}
private void setMiles(miles){
throw new IllegalAccessException("you're not allowed to change miles") ;
}
}
初始化和具名参数
class Robot{
def type,height,width
def access(location,weight,fragile){
println "Received fragile? $fragile, weight: $weight, loc: $location"
}
}
robot = new Robot(type:'arm',width: 0,height: 40)
println "$robot.type, $robot.height, $robot.width"
robot.access(x:30,y:20,z:10,50,true)
robot.access(50,true,x:30,y:20,z:10)
robot.access(50,x:30,y:20,z:10,true)
- 在构造对象时,可以简单得以逗号分隔的“名值对”来给出“属性值”。第一个形参必须定义为Map。要让这种操作正确执行,必须要有一个无参构造器(编译器会默认提供一个)。此操作会在无参构造方法之后执行。
- 在使用方法时,如果第一个是Map,则可以将这个映射中的键值对展开放在实参的列表中。如果实参的个数多于方法的形参的个数,而且多出来的实参是键值对,那么Groovy会假设方法的第一个形参是Map,然后将实参列表中的素有键值对组织到一起,作为第一个形参的值。之后,再将剩下的实参按照给出的顺序赋给其余的形参。
站在阅读代码的角度,真的很不建议上述的做法。我们可以明确将一个形参指定为Map。
def access(Map location,weight,fragile){
//TODO
}
可选形参
Groovy可以把构造方法和方法的形参设置为可选的。要定义可选形参,只需要在形参列表中给它赋一个值就可以了。Groovy还把最后一个为数组的形参视为可选的。
def log(x,base = 10){
Math.log(x) / Math.log(base)
}
println log(1024)
println log(1024,2)
def task(name,String[] details){
println "$name - $details"
}
task('Hing','150-0000-0000')
task('Hing','150-0000-0000','158-0000-0000')
task('Hing')
使用多赋值
- 要从方法返回多个结果,并将它们一次性赋给多个变量,我们可以返回一个数组,然后将多个变量以逗号分隔,放在圆括号中,置于赋值表达式左侧即可。
- 可以用此特性来交互变量,无需创建中间变量来保存被交换的值,只需将与交换的变量放在圆括号内,置于赋值表达式左侧,同时将它们以相反顺序放在方括号内,置于右侧即可。
- 如果有多余的变量,Groovy会将它们设置为null;多余的值则会被丢弃。
示例:
//返回多个结果
def splitName(fullName){
fullName.split(' ')
}
def (fistName,lastName) = splitName('Hing Kwan')
println "$fistName, $lastName"
//交换变量
def name1 = "Thomson"
def name2 = "Thompson"
println "$name1 and $name2"
(name1,name2) = [name2,name1]
println "$name1 and $name2"
//多的参数
def(String cat,String mouse) = ['Tom','Jerry','Spike','Tyke']
println "$cat and $mouse"
//多的结果
def(first,second,third) = ['Tom','Jerry']
println "$first and $second and $third"
实现接口
在Groovy中,可以把一个映射或一个代码块转化为接口,因此可以快速实现带有多个方法的接口。
Groovy没有强制实现接口中的所有方法:可以只定义自己关心的,而不考虑其他方法。如果剩下的方法从来不会被调用,那也就没有必要去实现这些方法了。
接口中的每个方法可能需要不同的实现,在Groovy中只需要创建一个映射,以每个方法的名字作为键,以对应的代码体作为值,同时使用简单的Groovy风格,用冒号(:)分隔方法名和代码块即可。
如果没有提供实现的方法被调用了,则会抛出NullPointerException。
布尔求值
根据上下文,Groovy会自动把表达式计算为布尔值。
以下是为true的条件
- Boolean:值为true
- Collection:集合不为空
- Character:值不为0
- CharSequence:长度大于0
- Enumeration:hasMoreElements()为true
- Iterator:hasNext() 为true
- Number:Double值不为0
- Map:该映射不为空
- Matcher:至少有一个匹配
- Object[]:长度大于0
- 其他任何类型:引用不为null
操作符重载
Groovy支持操作符重载。因为在Groovy中,每个操作符都会映射到一个标准的方法。
for(ch = 'a';ch < 'd';ch++){
println(ch)
}
for(ch in 'a'..'c'){
println(ch)
}
enum
示例一:
enum CoffeeSize {
SHORT, SMALL, MEDIUM, LARGE, MUG
}
def orderCoffee(size){
print "Coffee order received for size $size :"
switch (size){
case [CoffeeSize.SHORT,CoffeeSize.SMALL]:
println "you're health conscious"
break;
case CoffeeSize.MEDIUM..CoffeeSize.LARGE:
println "you gotta be a programmer"
break;
case CoffeeSize.MUG:
println "you should try Caffeine IV"
break;
}
}
orderCoffee(CoffeeSize.SMALL)
orderCoffee(CoffeeSize.LARGE)
orderCoffee(CoffeeSize.MUG)
print 'Available sizes are:'
for(size in CoffeeSize.values()){
print "$size";
}
示例二:
enum Methodologies{
Evo(5),
XP(21),
Scrum(30);
final int daysInIteration
Methodologies(days){
daysInIteration = days
}
def iterationDetails(){
println "${this} recommends $daysInIteration days for iteration"
}
}
for(methodology in Methodologies.values()){
methodology.iterationDetails()
}
变长参数
Groovy以两周方式支持变长参数。1)支持使用省略号标记形参;2)以数组作为末尾形参的方法。
def receiveVarArgs(int a,int ... b){
}
def receiveArray(int a,int[] b){
}
静态导入
import static Math.random as rand
import groovy.lang.ExpandoMetaClass as EMC
double val = rand();
def metaClass = new EMC(Integer);
一些特色的注解
@Canonical
可以条件地使用或者去掉字段。
import groovy.transform.Canonical
@Canonical(excludes = "lastName,age")
class Person{
String firstName;
String lastName;
int age;
}
def sara = new Person(firstName: "Sara",lastName: "Walker",age: 49)
println sara
@Delegate
使用@Delegate可以很方便地使用委托。
class Worker{
def work(){println 'get work done'}
def analyze(){println 'analyze...'}
def writeReport(){println 'get report written'}
}
class Expert{
def analyze(){println 'expert analysis...'}
}
class Manager{
@Delegate Expert expert = new Expert();
@Delegate Worker worker = new Worker();
}
def bernie = new Manager()
bernie.analyze()
bernie.work()
bernie.writeReport()
上面代码中因为Expert先被引入,所以analyze方法也先被引入,后面的就不引入相同的方法。
@Immutable
用@Immutable注解标记一个类,Groovy会将其字段标记为final,并且额外为我们创建一些便捷的方法。
使用@Immutable注解可以轻松创建轻量级不可变对象。不可变实例是作为消息传递的理想实例。
@Immutable
class CreditCard{
String cardNumber;
int creditLimit;
}
println new CreditCard("",1000)
@Lazy
使用@Lazy可以把耗时对象的构建推迟到真正需要时。可以自动将字段标记为volatile,并确保创建期间是线程安全的。
class Heavy{
def size = 10;
Heavy(){println "Creating Heavy with $size"}
}
class AsNeeded{
def value
@Lazy Heavy heavy1 = new Heavy()
@Lazy Heavy heavy2 = {new Heavy(size: value)}()
AsNeeded(){println "Created AsNeeded"}
}
def asNeeded = new AsNeeded(value: 100)
println asNeeded.heavy1.size
println asNeeded.heavy1.size
println asNeeded.heavy2.size
@Newify
可以用来创建实例。在创建DSL时,@Newify注解非常有用,它可以使得实例创建更像一个隐式操作。
@Newify([Person,CreditCard])
def fluentCreate(){
println Person.new(firstName: "John",lastName: "Doe",age: 20)
println Person(firstName: "John",lastName: "Doe",age: 20)
println CreditCard("",1000)
}
fluentCreate();
@Singleton
@Singleton(lazy = true,strict=false)
class TheUnique{
TheUnique(){
println 'TheUnique created'
}
def hello(){
println 'hello'
}
}
TheUnique.instance.hello()
TheUnique.instance.hello()
鸭子类型
def takeHelp(helper){
helper.helpMoveThings();
}
class Man{
void helpMoveThings(){
println "Man's helping"
}
}
class Woman{
void helpMoveThings(){
println "Woman's helping"
}
}
class Elephant{
void helpMoveThings(){
println "Elephant's helping"
}
}
takeHelp(new Man())
takeHelp(new Woman())
takeHelp(new Elephant())
多方法
如果一个类中有重载的方法,Groovy会聪明地选择正确的实现——不仅基于目标对象(调用方法的对象),还基于所提供的参数。因为方法分派基于多个实体——目标加参数,所以这被称为多分派或多方法。
ArrayList<String> lst = new ArrayList<>();
Collection<String> col = lst;
lst.add("One");
lst.add("Two");
lst.add("Three");
col.remove(0);
col.remove(0);
println lst;
println col;
静态类型检查
可以使用@TypeChecked注解进行静态类型检查。这个注解可以用于类或者单个方法上。如果用于一个类,则类型检查会在该类中所有方法、闭包和内部类上执行。如果用于一个方法,则类型检查仅在目标方法的成员上执行。
要利用静态类型检查,必须要指明方法和闭包的形参类型。
闭包
闭包基础
先从传统的方式看起,也许在我们编码的过程中,会有以下代码:
def sum(n){
total = 0
for(int i = 2; i <= n ;i+=2){
total += i
}
total
}
def product(n){
prod = 1
for(int i = 2 ; i <= n; i+=2){
prod *= i
}
total
}
sum(10);
product(10);
用闭包实现为:
def pickEven(n,block){
for(int i = 2; i <= n; i++){
block(i);
}
}
pickEven(10,{println it});
就像上面的调用一样,代码块({}内的代码)被传给形参block,也就是block代表了传入的代码块执行。
如果闭包是最后一个实参,调用可以用下面的优雅的写法:
pickEven(10){println it}
所以,当闭包是方法调用的最后一个实参时,可以将闭包附到方法调用上。这种情况下,代码块看上去就像是附在方法调用上的寄生虫。Groovy的闭包不能单独存在,只能附到一个方法上,或是赋值给一个变量。
上面代码中的it代表的意思:如果只向代码中传递一个参数,那么可以使用it这个特殊的变量名来指代该参数。我们也可以使用其他名称来代替it,如:
pickEven(10){evenNum -> println evenNum}
闭包有两个非常擅长的领域:1)辅助资源清理;2)辅助创建内部的领域特定语言
普通函数在实现某个目标明确的任务时优于闭包。重构的过程是引入闭包的好时机。
闭包的使用
将闭包赋值给变量并复用
def totalSelectValues(n,closure){
total = 0
for(i in 1..n){
if(closure(i)){
total += i
}
}
total
}
println totalSelectValues(10){it % 2==0}
def isOdd = {it % 2 != 0}
println totalSelectValues(10,isOdd)
class Equipment{
def calculator
Equipment(calc){
calculator = calc
}
def simulate(){
println "Running simulation"
calculator()
}
}
def eq1 = new Equipment({ println "Calculator 1"})
def aCalculator = { println "Calculator 2"}
def eq2 = new Equipment(aCalculator)
def eq3 = new Equipment(aCalculator)
eq1.simulate();
eq2.simulate();
eq3.simulate();
向闭包传递多个参数
def tellFortune(closure){
closure new Date("07/29/2016"),"Your day is filled with ceremony"
}
tellFortune(){
date,fortune -> println "Fortune for ${date} is '${fortune}'"
}
//以上的代码其实也就是下面的代码:
def tellFortune1(closure){
closure(new Date("07/29/2016"),"Your day is filled with ceremony")
}
tellFortune1({date,fortune -> println "Fortune for ${date} is '${fortune}'"})
符号->将闭包的参数声明与闭包主体分隔开来。
使用闭包进行资源清理
def writer = new FileWriter("out.txt");
writer.write('..');
// 没有调用writer.close()
//当从闭包返回时,withWriter()会自动刷新并关闭这个流
new FileWriter('out.txt').withWriter {
writer -> writer.write('!')
}
Execute Around Method模式
编写一个Execute Around方法,它接受一个块作为参数。在这个方法中,把对该块的调用夹到对那对方法的调用之间。即先调用第一个方法,然后调用该块,最后调用第二个方法。方法的使用者不必担心这对动作,他们会自动被调用。我们甚至可以在Execute Around方法内处理异常。
class Resource {
def open() { println "opened..." }
def close() { println "closed..." }
def read() { println "read..." }
def write() { println "writed..." }
def static use(closure) {
def r = new Resource();
try {
r.open()
closure(r)
} finally {
r.close()
}
}
}
Resource.use {
res ->
res.read()
res.write()
}
闭包与协程
def iterate(n,closure){
1.upto(n){
println "In iterate with value ${it}"
closure(it)
}
}
println "Calling iterate"
total = 0
iterate(4){
total += it
println "In closure total so far is ${total}"
}
println "Done"
上面的代码中,每次调用闭包,我们会从上一次的调用中恢复total值。
科里化闭包
带有预绑定形参的闭包叫科里化闭包。
当对一个闭包调用curry()时,就是要求先绑定某些参数。在预先绑定了一个形参之后,调用闭包时就不必重复为这个形参提供实参了。
可以使用curry()方法科里化任意多个形参,但这些形参必须是从前面开始的连续若干个。也就是说,如果有n个形参,我们可以任意科里化前k个,其中 0 <= k <= n。
如果想科里化后面的形参,可以使用rcurry()方法。如果想科里化位于形参列表中间的形参,可以使用ncurry()方法,传入要进行科里化的形参的位置,同时提供相应的值。
def tellFortunesCurry(closure){
Date date = new Date();
postForturne = closure.curry(date)
postForturne "Your day is filled with ceremony"
postForturne "They're features,not bugs"
}
tellFortunesCurry(){
date,fortune ->
println "Fortune for ${date} is '${fortune}'"
}
动态闭包
在给闭包传递参数时,也有很多灵活性。可以动态地确定一个闭包期望的参数的数目和类型。
即便一个闭包没有使用任何参数,就像{}或{it}中这样,其实它也会接受一个参数(名字默认为it)。如果调用者没有向闭包提供任何值,则第一个形参(it)为null。如果希望闭包完全不接受任何参数,必须使用{->}这种语法:在->之前没有形参,说明该闭包不接受任何参数。
利用maximumNumberOfParameters和parameterTypes属性,我们可以动态地检查闭包,而且在实现某些逻辑时有了更大灵活性。
def completeOrder(amount,taxComputer){
tax = 0
if(taxComputer.maximumNumberOfParameters == 2){
tax = taxComputer(amount,6.05)
}else{
tax = taxComputer(amount)
}
println "Sales tax is ${tax}"
}
completeOrder(100){it * 0.0825}
completeOrder(100){amount,rate -> amount * (rate/100)}
def examine(closure) {
println "$closure.maximumNumberOfParameters parameter(s) given:"
for (aParameter in closure.parameterTypes) {
println aParameter.name
}
println "--"
}
examine() {}
examine() { it }
examine() { -> }
examine() { val1 -> }
examine() { Date val1 -> }
examine() { Date val1, val2 -> }
examine() { Date val1, String val2 -> }
闭包委托
this、owner和delegate是闭包的三个属性,用于确定由哪个对象处理该闭包内的方法调用。一般而言,delegate会设置为owner,但是对其加以修改。
def examiningClosure(closure){
closure()
}
examiningClosure(){
println "In First Closure"
println "class is "+ getClass().name
println "this is"+this +", super is" + this.getClass().superclass.name
println "owner is "+ owner+", super is "+owner.getClass().superclass.name
println "delegate is "+delegate+ ", super is "+delegate.getClass().superclass.name
examiningClosure(){
println "In First Closure"
println "class is "+ getClass().name
println "this is"+this +", super is" + this.getClass().superclass.name
println "owner is "+ owner+", super is "+owner.getClass().superclass.name
println "delegate is "+delegate+ ", super is "+delegate.getClass().superclass.name
}
}
闭包内的this指向的是该闭包所绑定的对象(正在执行的上下文)。在闭包内引用的变量和方法都会绑定到this,它负责处理任何方法调用,以及对任何属性或变量的访问。如果this无法处理,则转向owner,最后是delegate。
使用尾递归
def factorial(BigInteger number){
if(number == 1){
1
}else{
number * factorial(number - 1)
}
}
try {
println "factorial of 5 is ${factorial(5)} "
println "factorial of 5000 is ${factorial(5000)}"
}catch (Exception e){
println ${e.getClass().name}
}
以上代码在5000的时候会报错,那么有什么解决方法呢?
Groovy语言通过闭包上的一个特殊的trampoline()方法提供了解决方法。
def factorial
factorial = { int number,BigInteger theFactorial ->
number == 1 ? theFactorial : factorial.trampoline(number - 1, number * theFactorial)
}.trampoline()
println "factorial of 5 is ${factorial(5,1)}"
println "factorial of 5000 is ${factorial(5000,1)}"
上面代码中,factorial变量本身被赋值的就是在闭包上调用trampoline()方法的结果。
这种递归叫尾递归,因为方法中最后的表达式是结束递归或者是调用自身。
上面的代码可以优化为:
def factorial(int factorialFor){
def tailFactorial
tailFactorial = {
int number,BigInteger theFactorial = 1 ->
number == 1 ? theFactorial : tailFactorial.trampoline(number - 1, number * theFactorial)
}.trampoline()
tailFactorial(factorialFor)
}
使用记忆缓存改变性能
上面的代码是一种可以使用递归调用更高效地使用内存的技巧。而它的一个变种是将问题分解为可以多次重复解决的若干部分。在执行期间,我们将子问题的结果保存下来,当调用到重复的计算时,只需要简单地使用保存下来的结果。这样就避免了重复运行,因此极大地减少了计算时间。记忆化可以将一些算法的计算时间复杂度从输入规模(n)的指数级(O(k^n))减低到只有线性级(O(n))。
def rodPrices = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30]
def desiredLength = 27
@Immutable
class RevenueDetails {
int revenue
ArrayList splits
}
def cutRod(prices, length) {
if (length == 0) {
return new RevenueDetails(0, [])
} else {
def maxRevenueDetails = new RevenueDetails(Integer.MIN_VALUE, [])
for (rodSize in 1..length) {
def revenueFromSecondHalf = cutRod(prices, length - rodSize)
def potentialRevenue = new RevenueDetails(
prices[rodSize] + revenueFromSecondHalf.revenue,
revenueFromSecondHalf.splits + rodSize
)
if (potentialRevenue.revenue > maxRevenueDetails.revenue) {
maxRevenueDetails = potentialRevenue
}
}
maxRevenueDetails
}
}
timeIt desiredLength, { length -> cutRod(rodPrices, length) }
以上代码将花费我们很长时间去计算,优化如下
def cutRod
cutRod ={ prices, length ->
if (length == 0) {
return new RevenueDetails(0, [])
} else {
def maxRevenueDetails = new RevenueDetails(Integer.MIN_VALUE, [])
for (rodSize in 1..length) {
def revenueFromSecondHalf = cutRod(prices, length - rodSize)
def potentialRevenue = new RevenueDetails(
prices[rodSize] + revenueFromSecondHalf.revenue,
revenueFromSecondHalf.splits + rodSize
)
if (potentialRevenue.revenue > maxRevenueDetails.revenue) {
maxRevenueDetails = potentialRevenue
}
}
maxRevenueDetails
}
}.memoize()
在以上代码中,Groovy创建了Memoize类的一个专用实例。该实例中有一个指向所提供闭包的引用,还有一个结果的缓存。当我们调用该闭包时,该实例会在返回结果之前响应缓存下来。在随后的调用中,如果对应某个参数已经有了相应的缓存下来的结果值,则返回该值。
记忆化技术是以空间换取时间。如果大规模计算的话,内存的需求可能会急剧增长。Groovy给我们提供了一些其他的选择,如:采用LRU算法的memoizeAtMost(),可以设置缓存下限的memoizeAtLeast()、可以设置缓存上限以及下限的memoizeBetween()
字符串
在Groovy中,”” 和 ‘’都表示字符串。如果需要显式地创建一个字符,只需要输入 ‘a’ as char。
Groovy的字符串也是不可变的。可以使用[]操作符读取一个字符;不过不能修改。
def printClassIfnfo(obj){
println "class: ${obj.getClass().name}"
println "superclass: ${obj.getClass().superclass.name}"
}
val = 25;
printClassIfnfo("The Stock closed at ${val}")
printClassIfnfo(/The Stock closed at ${val}/)
printClassIfnfo("This is a simple String")
看下GString的惰性求值问题:
price = 684.71
company = "Google"
quote = "Today $company stock closed as $price"
println quote
stocks = [Apple:663.01,Microsoft:30.95]
stocks.each{
key,value ->
company = key
price = value
println quote
}
得到的结果是:
Today Google stock closed as 684.71
Today Google stock closed as 684.71
Today Google stock closed as 684.71
这样的结果明显不是我们想要的。程序修改如下:
companyClosure = { it.write(company) }
priceClosure = { it.write("$price") }
quote = "Today ${companyClosure} stock closed at ${priceClosure}"
stocks = [Apple: 663.01, Microsoft: 30.95]
stocks.each { key, value ->
company = key
price = value
println quote
}
重构改为:
quote = “Today ${->company} stock closed at ${-> price}”
stocks = [Apple: 663.01, Microsoft: 30.95]
stocks.each { key, value ->
company = key
price = value
println quote
}
可以通过将字符串包含在3个单引号内(’’’ … ‘’’)来定义多行字面常量。
langs = ['C++':'Stroustrup','java':'Gosling','Lisp':'McCarthy']
content = ''
langs.each {language,author ->
fragment = '''
<language name="${language}">
<author>${author}</author>
</language>
'''
content += fragment
}
xml = "<languages>${content}</languages>"
println xml
操作符“~”可以方便地创建RegEx模式。这个操作符映射到String类的negate()方法。
要定义一个RegEx,使用正斜杠,如:/[G|g]roovy/
要确定是否存在匹配使用=~
要精确匹配使用==~
集合
lst = [1,3,4,1,8,9,2,6]
println lst
println lst.getClass().name
println lst[0]
println lst[lst.size() - 1]
println lst[-1] //最后一个
println lst[-2]
println lst[2..5] //从位置2开始,连续4个
println lst[-6..-3]
sublst = lst[2..5]
println sublst.dump() //打印该实例的详细信息
sublst[0] = 55
println "After sublst[0]=55 lst = $lst"
lst.each { println it} //集合的迭代
//对每个元素进行操作,并返回集合
println lst.collect({it * 2})
println lst.find( {it == 2}) //查找
println lst.findAll({it == 2}) //查找所有
lst = ['Programming','In','Groovy']
count = 0
lst.each {count += it.size()}
println count
println lst.collect {it.size()}.sum() //与上面的代码效果一样
//inject对集合中的每个元素进行闭包调用
println lst.inject(0) {carryOver,element -> carryOver+element.size()}
println lst.join(' ')
lst[0] = ['Be','Productive'] //替换
println lst
lst = lst.flatten()
println lst
println lst - ['Productive','In'] //去除元素
println lst.reverse()
println lst.size()
println lst*.size()
Map
langs = ['C++':'Stroustrup','Java':'Gosling','Lisp':'McCarthy']
println langs.getClass().name
println langs['List']
println langs.Java
println langs.'C++'
langs.each {entry ->
println "Language $entry.key was authored by $entry.value"
}
langs.each{language,author ->
println "Language $language was authored by $author"
}
println langs.collect {language,author ->
language.replaceAll("[+]","P")
}
entry = langs.find{language,author ->
language.size() > 3
}
println entry.value
selected = langs.findAll {language,author ->
language.size() > 3
}
selected.each {key,value ->
println key + ',' +value
}
println langs.any {language,author -> //只要有一个符合
language =~ "[^A-Za-z]"
}
println langs.every {language,author -> //每个都符合
language =~ "[^A-Za-z]"
}
authors = langs.groupBy {it.value.split(' ')[0]}
authors.each {author,values ->
println "$author : ${values.collect {key,value -> value}.join(', ')}"
}