閉包是一塊代碼可以傳入另外一個地方,並且在終點處可以運行該代碼。用編程語言來描述,就是指可以把一個函數(類對象)打包傳入另一個函數(類對象)里,通常指客戶端調用處。java閉包主要是通過接口+內部類來實現,有兩種形式:接口+內部類和接口+局部內部類
一、接口+內部類
class Outer {
private class Inner implements Runable {
@Overide
public void run() {
System.out.println("Outer.Inner.run");
}
}
public Runable getRun() {
return new Inner();
}
}
把內部類Inner聲明為private,使外界無法訪問,getRun方法返回一個內部類的實例。在客戶端調用:
Runable run = new Outer().getRun();
run.run(); // 客戶端得到Inner實例,並可調用run方法,這就是延遲運行,並inner實例傳到外界客戶端里
二、接口+局部內部類:
class Outer {
public Runable getRun() {
class Inner implements Runable {
@Overide
public void run() {
System.out.println("Outer.getRun.Inner.run");
}
}
return new Inner();
}
}
這兩種方式都可實現閉包,而在客戶端調用內部類的過程,稱為回調。
下面列出與JS閉包的不同之處:
java:
class Outer {
public List<Runable> getRunList() {
List<Runable> list = new ArrayList(10);
for (int i = 0; i < 10; i++) {
int temp = i;
list.add(new Runable() {
@Override
public void run() {
System.out.print(temp + " ");
}
});
}
return list;
}
}
調用:
List<Runable> list = new Outer().getRunList();
for (Runable item : list) {
item.run();
}
// 循環輸出:0 1 2 3 4 5 6 7 8 9
javascript:
function Outer {
var tempList = [];
for (var i = 0; i < 10; i++) {
tempList[i] = function () {
return i;
}
}
return tempList;
}
調用方:
var list = Outer();
for (var i = 0; i < list.length; i++) {
Console.write(list[i]() + " "); // 閉包回調
}
// 輸出結果:10 10 10 10 10 10 10 10 10 10
由上可以看出,同樣的代碼邏輯,java和js執行結果不一樣。這是因為js變量作用域在for, if 和 { }塊時,它的作用域都超出塊體外,在閉包調用時,會取出最后一個值為10。而java作用不同,隨着塊級}結束而結束,所以調用時,都取的是i的副本(已經將對應值存入)。另外C#的委托也是實現閉包的一種。