java8闭包

java程序在1.8支持了闭包,但是在oracle官网又找不到官方资料,在翻了很多网站后写了下面这些东西

闭包(closure)

来自百度百科的定义

闭包就是能够读取其他函数内部变量的函数。例如在javascript中,只有函数内部的子函数才能读取局部变量,所以闭包可以理解成“定义在一个函数内部的函数“。在本质上,闭包是将函数内部和函数外部连接起来的桥梁。

来自wikipedia的定义

闭包,(英语:closure),这里指点集拓朴的术语:在拓扑空间里,子集S 的闭包是指由S 的所有点及S 的极限点所组成的一个集合;直观上来说,即为所有“靠近”S 的点所组成的集合。在子集S 的闭包内的点称为S 的闭包点。闭包的概念在许多方面能与内部的概念相对比。

如何理解闭包

闭包总体分为两个部分

  1. 函数本身
  2. 函数周围的环境

JavaScript闭包

等级1

指针关系

1
2
3
4
5
6
7
8

function func() {
// closure scope
var a = 10;
var lambdaFun = () => a;
a++;
console.log(lambdaFun());
}

在这段代码中,我们看到了一个变量a,然后由一个函数lambdaFun指向并维护了这个变量a,这段代码最终将会输出11;

JavaScript指向了一个范围;

等级2

作用域

我们可以继续扩展这段代码来理解:

1
2
3
4
5
6
7
8
9
10
11
12
function func() { 
// closure scope
var a = 10;
var map = new Map();
map.set('val', () => a);
map.set('inc', () => a++);
return map;
}
v = func();
console.log(v.get('val')()); // returns 10
console.log(v.get('inc')()); // returns 10
console.log(v.get('val')()); // returns 11

我们用v来接收func的结果,而在v = func();之后,func函数的作用域已经超出,但是闭包函数仍然存在,当我们调用v.get(‘inc’)()之后,对象a依然会发生变化。

java闭包

等级1

指针关系

1
2
3
4
5
6
7
8
9
public class Test{
public static void main(String[] args) {
int a = 10;
Thread t = new Thread(
() -> System.out.println(a)
);
//a++;
}
}

为什么有一行注释代码呢?实际上,如果将注释打开,代码将会无法通过编译,这是java闭包局限的局限之一,
只能在封闭范围内将变量转化为final的。

等级2

作用域

让我们针对JavaScript等级2的代码含义,转换为java代码实现

1
2
3
4
5
6
7
8
9
10
11
12
public class Test{
public static void main(String[] args) {
Map map=func();
}
public static Map<String, Supplier> func() {
int a = 10;
Map<String, Supplier> map = new HashMap<>();
map.put("val", () -> a);
//map.put("inc", () -> a++);
return map;
}
}

仍然带着一行注释,这一行代码依然不会通过编译,java会在执行() -> a之后得到a的副本
所以在运行() -> a++代码的时候实际上操作的是a的副本,如果变量a分配是在栈上发生的,那这太混乱了。让我们将变量分配在堆上

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class Test{
static int a = 10;

public static Map<String, Supplier> func() {
Map<String, Supplier> map = new HashMap<>();
map.put("val", () -> a);
map.put("inc", () -> a++);
return map;
}

public static void main(String[] args) {
Map<String, Supplier> map=func();
Supplier s1=map.get("val");
Supplier s2=map.get("inc");
Supplier s3=map.get("val");
System.out.println(s1.get());//10
System.out.println(s2.get());//10
System.out.println(s3.get());//11
}
}

我们在此时做到了闭包,使用Supplier的get方法操作了func()方法里的a变量(虽然这个变量是一个静态的类变量)

为什么java要这么做

对于JavaScript来说,闭包的意义在于覆盖/封闭变量,在其看来,函数func创建了一个对象,而我们直接操作了对象的属性

1
2
v = func();
console.log(v.get('val')());

而Java语言里,每个java类都是为了描述对象而存在的,我们为对象赋予了方法,外界通过选定对象和方法来修改属性;

相比而言,Java确实实现的不如JavaScript灵活,但是也避免了JavaScript里常常发生的灵异变化。