[14-01] 闭包
1、闭包的概念
所谓闭包,就是指“一个持有外部环境变量的函数”,与其说这是一种形式的函数,不如说这是一种程序结构。这个概念更多在JavaScript中提到,在JS中我们知道,函数是可以作为对象返回的,于是看下面这样的方法:
function foo(){
var local = 0
function bar(){
local++
console.log(local)
return local
}
return bar
}
9
1
function foo(){
2
var local = 0
3
function bar(){
4
local++
5
console.log(local)
6
return local
7
}
8
return bar
9
}
外层函数将保存了信息的可执行内层函数作为结果返回,即这里的foo()将保存了local局部变量信息的可执行内层函数bar()作为结果返回。所以刚才我们说的概念“一个持有外部环境变量的函数”,也就是指这种结构情况下的bar()了。
然后使用的时候可能就是这样:
var func = foo()
func()
2
1
var func = foo()
2
func()
打开你的Chrome,试着把这段代码执行一下,看看它有什么用,其实就是个计数器而已,每运行一次func(),就计数一次:
所以这个例子中闭包有什么用呢
- 隐藏变量:给你的方法func()中是有变量的,但是JS中不像Java有private权限修饰,可你能访问吗,能擅自修改吗?不能
2、Java中闭包的实现
好了,我们借用JS来理解一下“闭包”的概念,那么Java如何实现闭包呢,Java并不支持在方法中返回一个方法呀?在JS中是外层函数包含变量和内部函数,Java中其实就是类包含成员变量和内部类。因为内部类可以直接访问外部类的信息(即便是private),这就满足了“一个持有外部环境变量的 '函数' ”:
- 如何用变量去存储方法
- Java中能够保存方法的变量其实就是普通的对象
- 如何让这个对象能够访问所在类的自由变量
- 内部类,其能够访问外部类的所有属性和方法
那么上面那个JS的闭包计数器,用Java来写就是这样:
public class Foo {
//成员变量
private int local = 0;
//内部类
class Bar {
public int func() {
local++;
System.out.println(local);
return local;
}
}
//返回一个内部类的引用
public Bar getBar() {
return new Bar();
}
}
19
1
public class Foo {
2
//成员变量
3
private int local = 0;
4
5
//内部类
6
class Bar {
7
public int func() {
8
local++;
9
System.out.println(local);
10
return local;
11
}
12
}
13
14
//返回一个内部类的引用
15
public Bar getBar() {
16
return new Bar();
17
}
18
19
}
public class Test {
public static void main(String[] args) {
Foo.Bar bar = new Foo().getBar();
bar.func(); //打印1
bar.func(); //打印2
bar.func(); //打印3
}
}
8
1
public class Test {
2
public static void main(String[] args) {
3
Foo.Bar bar = new Foo().getBar();
4
bar.func(); //打印1
5
bar.func(); //打印2
6
bar.func(); //打印3
7
}
8
}
更多时候我们采用“接口+匿名内部类”的形式,面向接口更加灵活(给外部留出通道,即可以接收这个内部类),同时更好地隐藏内部类,所以我们再修改一下:
public interface Bar {
int func();
}
3
1
public interface Bar {
2
int func();
3
}
public class Foo {
//成员变量
private int local = 0;
//匿名内部类
class Inner implements Bar {
@Override
public int func() {
local++;
System.out.println(local);
return local;
}
}
//返回一个匿名内部类的引用
public Bar getBar() {
return new Inner();
}
}
20
1
public class Foo {
2
//成员变量
3
private int local = 0;
4
5
//匿名内部类
6
class Inner implements Bar {
7
8
public int func() {
9
local++;
10
System.out.println(local);
11
return local;
12
}
13
}
14
15
//返回一个匿名内部类的引用
16
public Bar getBar() {
17
return new Inner();
18
}
19
20
}
public class Test {
public static void main(String[] args) {
Bar bar = new Foo().getBar();
bar.func();
bar.func();
bar.func();
}
}
8
1
public class Test {
2
public static void main(String[] args) {
3
Bar bar = new Foo().getBar();
4
bar.func();
5
bar.func();
6
bar.func();
7
}
8
}
3、一个示例
Teachable接口和Programmer基类都提供了work方法,现在有个人既是老师也是程序员,即他要继承Programmer类又要实现Teachable接口,如何处理work方法同名的问题?闭包。实际上,下面的例子稍作修改,也同样适用于委婉的多继承。
public interface Teachable {
public void work();
}
3
1
public interface Teachable {
2
public void work();
3
}
public class Programmer {
private String name;
public Programmer() {
}
public Programmer(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void work() {
System.out.println(name + " is coding right now.");
}
}
23
1
public class Programmer {
2
private String name;
3
4
public Programmer() {
5
}
6
7
public Programmer(String name) {
8
this.name = name;
9
}
10
11
public String getName() {
12
return name;
13
}
14
15
public void setName(String name) {
16
this.name = name;
17
}
18
19
public void work() {
20
System.out.println(name + " is coding right now.");
21
}
22
23
}
public class TeachableProgrammer extends Programmer {
public TeachableProgrammer() {
}
public TeachableProgrammer(String name) {
super(name);
}
public void work() {
super.work();
}
public void teach() {
getCallbackReference().work();
}
private class Inner implements Teachable {
@Override
public void work() {
System.out.println(TeachableProgrammer.this.getName() + " is teaching right now.");
}
}
public Teachable getCallbackReference() {
return new Inner();
}
}
29
1
public class TeachableProgrammer extends Programmer {
2
3
public TeachableProgrammer() {
4
}
5
6
public TeachableProgrammer(String name) {
7
super(name);
8
}
9
10
public void work() {
11
super.work();
12
}
13
14
public void teach() {
15
getCallbackReference().work();
16
}
17
18
private class Inner implements Teachable {
19
20
public void work() {
21
System.out.println(TeachableProgrammer.this.getName() + " is teaching right now.");
22
}
23
}
24
25
public Teachable getCallbackReference() {
26
return new Inner();
27
}
28
29
}
public class Test {
public static void main(String[] args) {
TeachableProgrammer person = new TeachableProgrammer("David");
person.work();
person.teach();
}
}
//David is coding right now.
//David is teaching right now.
x
1
public class Test {
2
public static void main(String[] args) {
3
TeachableProgrammer person = new TeachableProgrammer("David");
4
person.work();
5
person.teach();
6
}
7
}
8
9
//David is coding right now.
10
//David is teaching right now.
4、内部类的意义小结
- 更好的封装和信息隐藏
- 可以访问外部类的所有元素,实现闭包
- 可以避免修改接口而实现同一个类中两种同名方法的调用
- 间接实现多重继承