在代碼塊中用def關鍵字定義的變量不能在外部訪問,如:
try{
def msg="Hello,world!";
}
//pringln msg;
如果運行最後一行"println msg;",則會報錯。
但是,在代碼塊中沒有用def關鍵字聲明的變量,就可以在外部訪問,如:
try{
msg="Hello,world!";
}
println msg;
已經使用def定義的變量,不能再次用def定義:
def a=10;
//def a=20;
代碼塊是可以嵌套的:
try{
try{
try{
println "Hello,world!";
}
}
}
二、閉包
閉包是可以引用外部上下文環境的一系列語句。可以將閉包賦予一個變量,並在稍後執行。
每個閉包都有一個返回值,默認的返回值就是該閉包中最後一行語句的結果
閉包可以訪問外部的變量
def x=1;
def b={
x*2;//閉包可以訪問外部的變量
}
將閉包賦予一個變量將記住該閉包創建時的上下文環境,即使運行時已經超出了原來的作用域:
def c;
try{
def m=10;
c={m};
}
println c();
在此處調用c(),即使已經超出了block的作用域,但仍能輸出m的值
將一個閉包放入另一個閉包,可以創建它的兩個實例:
c={e={"Hello,World";};e;}
v1=c();
v2=c();
println v1==v2//在此得出false,說明c()返回了不同的實例
可以在定義閉包時聲明一些參數,之後在調用閉包時將這些參數值傳入閉包:
c={n-> println "Parameter="+n;}
c(10);
可以利用參數將信息從包內導出,也就是所謂的“輸出參數”
c={o,p-> o<<p};
x=[];
c(x,1);
c(x,2);
c(x,3);
println x;
在閉包中,總是可以用到一個叫做it的變量,如果沒有顯式的定義參數,則可以用it來引用
[quote]c={"Parameter=${it}"};
println c(10);[/quote]
如果沒有傳入任何參數,it仍然存在,但是爲null
閉包的參數不能使用作用域中已存在變量的名字,除了隱式變量it:
def it=10;
c={it*it};
println c(5);//在此,隱式變量it仍然引用參數5,而非外部定義的10
參數在調用閉包時可以被命名。這些被命名的參數將被組裝進一個Map,並指定給閉包的第一個參數:
def x={a,b,c->a.m+a.n+a.o-b+c;};
println x(m:10,5,n:15,6,o:20);
在調用閉包x的時候,參數m:10,n:15,o:20被組裝進Map賦予形參a,5、6按順序分別賦予形參b和c,結果是46。
我們可以在閉包內部和外部查詢參數的個數:
def c={a,b,c->getMaximumNumberOfParameters();}
println c(1,2,3);
println c.getMaximumNumberOfParameters();
調用閉包的時候,可以對最後的一個或幾個參數應用默認值:
def c={a,b=2,c=3->a+b+c;};
println c(1);
結果輸出6
一個閉包可以在最後一個參數前添加Object[]前綴來創建可變長度的參數:
def c={a,Object[] b->a+b[0]+b[1];}
println c(1,2,3);
我們可以使用一個list參數調用閉包。如果閉包沒有定義一個list類型的參數,則此參數將作爲分解的參數傳入閉包:
def c={a,b,c->a+b+c};
println c([1,2,3]);
閉包可以通過將第一個或前幾個參數固化爲常量,使用curry方法進行拷貝:
def c={a,b,c->a+b+c};
def d=c.curry(1);//拷貝閉包c,並將第一個參數a固化爲常量1
println d(2,3);//調用閉包d,只需要給出剩餘的參數。實際使用的參數仍然是1,2,3
在閉包中,可以結合使用curry和不定長度參數:
def c={a,Object[] b->b.each({m->a=a+m;});a;};
def d=c.curry(1);
def e=d.curry(2,3);//實際參數爲1,[2,3]
def f=e.curry(4);//實際參數爲1,[2,3,4]
println f(5);//實際參數爲1,[2,3,4,5]
閉包可以遞歸調用
三、函數
首先,必須使用def關鍵字定義函數,函數也不能嵌套使用;
其次,函數不能訪問在外部通過def定義的變量:
def c=10;
def f(){
println c;//編譯通過
}
//f();//運行時報錯
還可以使用特殊的語法標記&將函數賦予新的命名:
def f(){
println "Hello,world!";
}
def s=this.&f;
s();
函數可以後參數,包括輸入參數和輸出參數;
如果兩個函數的擁有不同個數的未指定類型的參數,則可以使用相同的名稱
def f(a){
println a*a;
}
def f(a,b){
println a*b;
}
f(10);
f(5,10)
函數可以有返回值:
1.如果有顯示地使用return關鍵字,則返回return指定的返回值,其後面的語句不再執行;
2.如果沒有顯式地使用return關鍵字,則返回函數最後一行語句的運行結果;
3.如果使用void關鍵字代替def關鍵字定義函數,則函數的返回值將爲null。
如果一個函數與一個閉包擁有相同的名稱和參數,則調用的時候將執行函數,而不是閉包:
def f(a){
return a*10;
}
def f={a->a*20};
println f(5);//輸出50而不是100
函數可以接受閉包作爲參數:
def f(a,Closure c){
c(a);
}
f(10){println "Parameter a = "+it;}
與閉包相同,函數可以爲參數指定默認值:
def f(a,b=10){
return a+b;
}
println f(5);//輸出15
函數也會將命名的參數包裝進一個Map,並賦予第一個參數:
def f(a,b){
return b+a.x+a.y;
}
println f(x:2,1,y:3);//輸出6
函數與閉包的相同之處還有,函數同樣支持不定長度的參數、分解list作爲參數,以及遞歸調用。