首先,什么是闭包?闭包是一个可调用的对象,它记录了 一些信息,这些信息用于创建它的作用域。内部类其实就是OOP的闭包,因为它不仅含有外围类对象(用于创建内部类的作用域)的信息,还自动拥有一个指向此外围类对象的引用,在此作用域内,内部类有权操作所有成员,包括private成员。
在JAVA中不存在像C++那样的指针,也就是说JAVA不允许回调。通过回调,对象可以携带一些信息,这些信息允许该对象在稍后的某个时刻调用初始对象,这在编程中其实非常有用,回调也往往是通过指针来实现的,但在JAVA中明确禁止了指针的使用,因为在JAVA看来,指针的权限过于大,非常不安全。
所以在JAVA中采用通过内部类提供闭包的功能来代替指针的功能,且内部类比指针更加安全灵活,看下面一段代码:
package access;
interface Incrementable{
void increment();
}
class Callee1 implements Incrementable{
private int i = 0;
@Override
public void increment() {
// TODO Auto-generated method stub
i++;
System.out.println(i);
}
}
class MyIncrement{
public void increment(){
System.out.println("Other operation");
}
static void f(MyIncrement mi){
mi.increment();
}
}
class Callee2 extends MyIncrement {
private int i = 0;
public void increment(){
super.increment();
i++;
System.out.println(i);
}
private class Closure implements Incrementable{
@Override
public void increment() {
// TODO Auto-generated method stub
Callee2.this.increment();
}
}
Incrementable getCallbackReference(){
return new Closure();
}
}
class Caller{
private Incrementable callbackReference;
Caller(Incrementable cbh){
callbackReference = cbh;
}
void go(){
callbackReference.increment();
}
}
public class Callbacks {
public static void main(String[] args) {
// TODO Auto-generated method stub
Callee1 c1 = new Callee1();
Callee2 c2 = new Callee2();
MyIncrement.f(c2);
Caller caller1 = new Caller(c1);
Caller caller2 = new Caller(c2.getCallbackReference());
caller1.go();
caller1.go();
caller2.go();
caller2.go();
}
}
此程序的输出结果为:
上述例子再一次展示了外围类实现一个借口与内部类实现接口的区别。Callee1是简单的解决方式;Callee2继承自MyIncrement,由于在MyIncrement中已经有了一个不同的Increment方法,并且与接口Incrementable的Increment方法完全不相关,所以在Callee2中不能再次为了使用Incrementable中的Increment方法而覆盖掉MyIncrement中的Increment方法,所以只能使用内部类独立实现Incrementable中的Increment方法,值得注意的是,在创建了一个内部类时,并没有在外围类的接口中添加东西,也没有修改外围类的接口。
在Callee2中除了getCallbackReference外,其余成员均为private,若想建立与外界的联系,interface Incrementable是必须的。
内部类实现了Incrementable,提供了一个返回Callee2的“指针”,而且是一个安全的“指针”,无论是谁获得了该“指针”,都只能调用increment方法,除此之外没有任何其它功能,并不像真正的指针那样拥有很多权限来让我们做更多的事情,这也在一定程度上避免了很多问题。
Caller的构建器需要一个Incrementable的引用作为参数,然后在以后的某个时刻Caller对象可以使用此引用回调Caller类。
回调的价值就在于灵活性:可以在运行时动态决定需要调用什么方法。