现在的位置: 首页 > 综合 > 正文

内部类(闭包与回调)

2018年12月12日 ⁄ 综合 ⁄ 共 3454字 ⁄ 字号 评论关闭

http://jiangzhengjun.iteye.com/blog/658354

动态语言的闭包是一个永恒的话题。闭包在编码过程的方便和快捷使得动态语言的拥护者对它津津乐道,而静态语言特别是Java语言的扇子们会拿出匿名内部类来说Java语言也有类似的功能。

 

JavaScript 中闭包的产生是由于 JavaScript 中允许内部 function,也就是在一个 function 内部声明的 function 。内部 function 可以访问外部 function 中的局部变量、传入的参数和其它内部 function 。当内部 function 可以在包含它的外部 function 之外被引用时,就形成了一个闭包。这个时候,即便外部 function 已经执行完成,该内部 function 仍然可以被执行,并且其中所用到的外部 function 的局部变量、传入的参数等仍然保留外部
function 执行结束时的值。下面是一个例子:

function Outer(){
    var i=0;
    function Inner(){
        alert(++i);
    }
    return Inner;
}
var inner = Outer();
inner();

因为函数Outer外的变量inner引用了函数Outer内的函数Inner,就是说:当函数Outer的内部函数Inner被函数Outer外的一个变量inner引用的时候,就创建了一个闭包。


闭包有什么作用:简而言之,闭包的作用就是在Outer执行完并返回后,闭包使得Javascript的垃圾回收机制GC不会收回Outer所占用的资源,因为Outer的内部函数Inner的执行需要依赖Outer中的变量。


闭包是一个可调用的对象,它记录了一些信息,这些信息来自于创建它的作用域。通过这个定义,可以看出内部类是面向对象的闭包,因为它不仅包含创建内部类的作用域的信息,还自动拥有一个指向此外围类对象的引用,在此作用域内,内部类有权操作所有的成员,包括private成员。

 

C++有指针函数,可以实现回调。通过回调,对象能够携带一些信息,这些信息允许它在稍后的某个时刻调用初始的对象。Java中没有指针,回调是通过匿名类来实现的。

 

 

>>>回调的一种理解<<<

 

回调的基本原理跟好莱坞原则一样,Don't call me,I'll call you.


编程上来说,一般使用一个库或类时,是你主动调用人家的API,这个叫Call,有的时候这样不能满足需要,需要你注册(注入)你自己的程序(比如一个对象),然后让人家在合适的时候来调用你,这叫Callback。设计模式中的Observer就是例子:所有的观察者都需要向自己关心的主题Observable注册,然后主题在适当时机(主题类对象的属性发生变化时)通知所有订阅它的观察者并更新,其中观察者都实现了一个统一的Observer接口中的Update方法。

回调可以这样具体的想:

我提供一个方法,在另一个对象中注册,至于这个方法什么时候用,就要看这个对象怎么决定了,当这个对象调用这个方法时,就叫做回调

/**下面应用中ICallBack接口与Printer类好比是别人提供的API,*/
public interface ICallBack {//回调接口
	public void print();
}

public class Printer {//API功能实现类
	ICallBack ic;

	void setCallBack(ICallBack ic) {
		this.ic = ic;
	}

	/* 供外界调用,即自己提供一个接口ICallBack,由外界PrintHandler去实现,再在适当时机回
	 * 头调用外界所提供的实现print方法。我没有实现接口,但是我取得了一个实现接口的对象,而
	 * 这个对象是外界类调用我的方法setCallBack()时所赋给我的,因此我可以在业务需要的地方
	 * 来调用外界所提供的实现print方法
	 */
	void execute() {
		//... 固定算法 do some thing
		ic.print(); //抽取变化的部分,由外界去实现
		//... 固定算法 do some thing
	}
}

/**下面是外界应用*/
public class PrintHandler {
	public static void main(String[] args) {
		Printer printer = new Printer();
		/*
		 * 注意下面的这项代码片段,它给printer对象传递了一个实现ICallBack接口的匿名类,这
		 * 样Printer类的对象就取得了一个实现回调接口的类,因此Printer可以在任何时候调用接
		 * 口中的方法
		 */
		printer.setCallBack(new ICallBack() {
			/* 
			 * print 方法在PrintHandler类中实现,但不在PrintHandler 类对象中调用,而是
			 * 在Printer类对象中调用,这就是回调
			 */
			public void print() {
				System.out.println("This is a callback");
			}
		});
		//  这句话可以设置成当满足某条件时再执行   
		printer.execute();
	}
}

>>>回调的另一种理解<<<


在以下Adjustable接口和Base类中都定义了adjust()方法,这两个方法的参数签名相同,但是有着不同的调节功能。

interface Adjustable {
	//功能:调节温度
	public void adjust(int temperature);
}

class Base {
	private int speed;

	//功能:调节速度
	public void adjst(int spped) {
		this.speed = speed;
	}
}

可见虽然方法名同,但功能不同。

 

如果有一个Sub类同时具有调节温度和调节速度的功能,那么Sub类需要继承Base类,并实现Adjustable接口,但是以下代码并不能满足这一需求:

class Sub extends Base implements Adjustable {
private int temperature;

public void adjust(int temperature) {
this.temperature = temperature;
}
}

以上Sub类实现了Adjustable接口中的adjust()方法,并且把Base类中的adjust()方法覆盖了,这意味着Sub类仅仅有调节温度的功能,但失去了调节速度的功能。或以使用内部类来解决这一问题:

class Sub extends Base {
	private int temperature;

	// 调节温度的真真实现在外部内中,但不提供给外部,只能通过内部类回调
	private void adjustTemperature(int temperature) {
		this.temperature = temperature;
	}

	// 实现回调接口
	private class Closure implements Adjustable {
		public void adjust(int temperature) {
			// 通过回调外部类中的实现来完成温度调节功能
			adjustTemperature(temperature);
		}
	}

	// 获取回调节引用
	public Adjustable getCallBackReference() {
		return new Closure();
	}

	public static void main(String[] args) {
		Sub sub = new Sub();
		// 具有调节速度的功能
		sub.adjst(1);
		/*
		 *  回调: 又具有调节温度的功能
		 *  客户类先调用sub实例的getCallBackReference()方法,获得
		 *  内部类的Closure实例,然后再调用Closure实例的adjust()方
		 *  法,该方法又调用Sub实例的adjustTemperature()方法。这种
		 *  调用过程称为回调。
		 */
		sub.getCallBackReference().adjust(2);
	}
}

上面使Sub类既不覆盖Base类的adujst()方法,又实现了Adjustable接口的adjust()方法。

 

回调实质上是指一个类尽管实际上实现了某种功能,但是没有直接提供相应的接口,客户类可以通过这个类的内部类的接口来获得这种功能。而这个内部类本身并没有提供真正的实现,仅仅调用外部类的实现。可见,回调充分发挥了内部类所具有的访问外部类的实现细节的优势。

 

观察者模式也符合这一种理解:请参见XXXXXXXXXXXXX

抱歉!评论已关闭.