概念:
我觉得在理解一个事物之前,需要对这个事物在我们的大脑里有一个初步的概念,然后再对这个概念补充上细节,这是我在理解一些陌生事物的时候的一个方法,也可以说是类比理解法吧。先说闭包~
一.闭包
闭包,故名思意就是,把一个包关起来,那么对于Java来说,这个包就是类了,因为在java中任何事物都是类,都是对象。那么闭包,直接理解上就是把一个类封装起来(封装就是包装差不多的意思)。然后结合一下,闭包内容放在内部类中,所以闭包就是用一个类把另一个类包装起来,说起来这和内部类没有什么不同点啊,为啥要专门用一个词来表述它呢?因为这个闭包还有很多其他的作用。而且其构造要比内部类复杂一点,先说说它的作用,作用有二~
1. 闭包能够保护内部类里面的变量安全,不会被外部访问
2. 闭包能够维持一个变量一直存活在内存中,不被CG(垃圾回收机制)回收掉
在构造上,内部类需要提供一个给外部调用它的接口,这样才能在维持住内部类的同时,因为内部类携带了外部类的信息,所以外部类也得以存活。
二.回调
回调直接理解就是回头调用,先将相关的方法实现好,但是并不由我来决定什么时候来调用它,而是等到一个时候,程序自己回头调用这个方法,而实现回调机制,这可以说是一种设计模式,而不仅仅是一个语言上的特性。与回调相关的概念还有同步调用,与异步调用,同步调用即单向调用,调用方等待对方执行完成后才返回。异步调用则类似消息机制,等待收到一定的消息后执行某些操作,回调与异步调用有一些共同之处,现在理解的还不是很清楚,先埋下一坑,以后清楚了在补一篇。
实例:
接下来就直接上代码分析,让大家补充一些细节上的理解,来清楚整个过程,整个过程相当于我的口述,如有不对的地方,希望大家指出:
interface Incrementable{
void increment();
}
class Callee1 implements Incrementable{
private int i = 0;
@Override
public void increment(){
i++;
System.out.println(i);
}
}
class MyIncrementable {
public void increment(){ System.out.println("Other Operarion"); }
static void f(MyIncrementable mi){ mi.increment(); }
}
class Callee2 extends MyIncrementable{
private int i = 0;
@Override
public void increment(){
super.increment();
i++;
System.out.println(i);
}
private class Closure implements Incrementable{
@Override
public void increment(){
Callee2.this.increment();
}
}
Incrementable getCallbackReference(){
return new Closure();
}
}
class Caller{
private Incrementable callbackReference;
Caller(Incrementable cbn){ callbackReference = cbn; }
void go(){ callbackReference.increment(); }
}
public class Callbacks {
public static void main(String[] args){
Callee1 c1 = new Callee1();
Callee2 c2 = new Callee2();
MyIncrementable.f(c2);
Caller caller1 = new Caller(c1);
Caller caller2 = new Caller(c2.getCallbackReference());
caller1.go();
caller1.go();
caller2.go();
caller2.go();
}
}
这个是java编程思想上很经典的一个例子。输出是这样的:
Other Operarion
1
1
2
Other Operarion
2
Other Operarion
3
希望大家首先自己通读一下代码,然后理解一下程序输出结果为什么是这样的,这样有助于我们去理解之前所说的闭包与回调机制。
这里我默认大家看完了,我来说一下我的理解:
首先Callee1是一个简单的实现了接口Incrementable与相关方法,在这里起到一个对比的作用而已。然后实现了一个MyIncrement类同样实现了一个increment()方法但是这个与接口中的increment()没有任何关系,因为这个类自己实现的,并没有实现这个接口,而静态方法f()也只是为了测试一下increment()方法。而Callee2继承自这个类。这里就是重点了。同样写了一个increment()方法,覆盖了父类方法,但是中间还是调用了父类方法。接下里是一个内部类也就是闭包的具体实现了。内部类实现了接口Incrementable并且直接调用外部类的方法作为具体的实现。内部类实现Increment able接口很关键,这样就给外部留下了一个通道,能够接受这个内部类。最后Callee2的后面留下了一个钩子,即getCallbackReference()方法,它返回一个内部类的对象,实现了内部与外部的链接,同时有保证了内部类的安全,因为只有Callee2的对象可以访问与调用这个内部类的方法,而其他的类都无权访问,即使是基类接口对象。而后面的Caller类起到的是一个唤醒作用,通过接受不同的接口对象,实现不同的操作,但还有一个作用是等待接受一个内部类对象,来产生回调。现在大家再回头看一下输出就能够明白了。
假装你回头看了,在main()方法中,首先是创建对象与声明,然后是调用了一个MyIncrement的静态方法,传入的是一个Callee2对象,此时无法触发回调,所以只是正常的输出,然后,才Caller2的初始化时传入的是一个Closure对象从而产生了回掉。
以上就是java的闭包与回调机制,结合后面的内容会有更多意想不到的作用~
========================================================
附:
interface OrderResult {
/**
* 订购货物的状态
*
* @param state
* @return
*/
//参数可以不用, 用不用按照自己的实际需求决定
public String getOrderResult(String state);
}
class Store {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
Store(String name) {
this.name = name;
}
/*回调函数, 将结构传给那个我们不能直接调用的方法, 然后获取结果*/
public String returnOrderGoodsInfo(OrderResult order) {
String[] s = {"订购中...", "订购失败", "即将发货!", "运输途中...", "已在投递"};
Random random = new Random();
int temp = random.nextInt(5);
String s1 = s[temp];
return order.getOrderResult(s1);
}
}
//同步回调
class SyncBuyer implements OrderResult {
private Store store;//商店
private String buyerName;//购物者名
private String goodsName;//所购商品名
public Store getStore() {
return store;
}
public void setStore(Store store) {
this.store = store;
}
public String getBuyerName() {
return buyerName;
}
public void setBuyerName(String buyerName) {
this.buyerName = buyerName;
}
public String getGoodsName() {
return goodsName;
}
public void setGoodsName(String goodsName) {
this.goodsName = goodsName;
}
SyncBuyer(Store store, String buyerName, String goodsName) {
this.store = store;
this.buyerName = buyerName;
this.goodsName = goodsName;
}
/*调用从商店返回订购物品的信息*/
public String orderGoods() {
String goodsState = store.returnOrderGoodsInfo(this);
System.out.println(goodsState);
myFeeling();// 测试同步还是异步, 同步需要等待, 异步无需等待
return goodsState;
}
public void myFeeling() {
String[] s = {"有点小激动", "很期待!", "希望是个好货!"};
Random random = new Random();
int temp = random.nextInt(3);
System.out.println("我是" + this.getBuyerName() + ", 我现在的感觉: " + s[temp]);
}
/*被回调的方法, 我们自己不去调用, 这个方法给出的结果, 是其他接口或者程序给我们的, 我们自己无法产生*/
@Override
public String getOrderResult(String state) {
return "在" + this.getStore().getName() + "商店订购的" + this.getGoodsName() + "玩具, 目前的预订状态是: " + state;
}
}
//异步回调
class NoSyncBuyer implements OrderResult {
private Store store;//商店
private String buyerName;//购物者名
private String goodsName;//所购商品名
public Store getStore() {
return store;
}
public void setStore(Store store) {
this.store = store;
}
public String getBuyerName() {
return buyerName;
}
public void setBuyerName(String buyerName) {
this.buyerName = buyerName;
}
public String getGoodsName() {
return goodsName;
}
public void setGoodsName(String goodsName) {
this.goodsName = goodsName;
}
NoSyncBuyer(Store store, String buyerName, String goodsName) {
this.store = store;
this.buyerName = buyerName;
this.goodsName = goodsName;
}
/*调用从商店返回订购物品的信息*/
public String orderGoods() {
String goodsState = "--";
MyRunnable mr = new MyRunnable();
Thread t = new Thread(mr);
t.start();
System.out.println(goodsState);
goodsState = mr.getResult();// 得到返回值
myFeeling();// 用来测试异步是不是还是按顺序的执行
{//等待异步完成(测试)
for (int i = 0; i < 20; i++) {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
goodsState = mr.getResult();
if (goodsState != null) {
return goodsState;
}
}
}
return goodsState;
}
public void myFeeling() {
String[] s = {"有点小激动", "很期待!", "希望是个好货!"};
Random random = new Random();
int temp = random.nextInt(3);
System.out.println("我是" + this.getBuyerName() + ", 我现在的感觉: " + s[temp]);
}
/*被回调的方法, 我们自己不去调用, 这个方法给出的结果, 是其他接口或者程序给我们的, 我们自己无法产生*/
@Override
public String getOrderResult(String state) {
return "在" + this.getStore().getName() + "商店订购的" + this.getGoodsName() + "玩具, 目前的预订状态是: " + state;
}
// 开启另一个线程, 但是没有返回值, 怎么回事
// 调试的时候, 等待一会儿, 还是可以取到值, 但不是立即取到, 在print显示的时候, 却是null, 需要注意?
private class MyRunnable implements Runnable {
private String result;
public String getResult() {
return result;
}
public void setResult(String result) {
this.result = result;
}
@Override
public void run() {
try {
Thread.sleep(10000);
result = store.returnOrderGoodsInfo(NoSyncBuyer.this);// 匿名函数的时候, 无法return 返回值
} catch (InterruptedException e) {
throw new RuntimeException("出大事了, 异步回调有问题了");
}
}
}
}
class TestCallback {
public static void main(String[] args) {
Store wallMart = new Store("沙中路沃尔玛");
SyncBuyer syncBuyer = new SyncBuyer(wallMart, "小明", "超能铁扇公主");
System.out.println(syncBuyer.orderGoods());
System.out.println("\n");
Store lawson = new Store("沙中路罗森便利店");
NoSyncBuyer noSyncBuyer = new NoSyncBuyer(lawson, "cherry", "变形金刚");
System.out.println(noSyncBuyer.orderGoods());
}
}