回调函数小对比

JavaScript 中函数是 Function 对象,因此可以很方便的将函数作为参数传递给另一个函数,这大大方便了异步编程。最简单的例子:

function foo(callback) {
	if (typeof callback === "function") {
		callback("hi there");
	}
}

foo(function(param) {
	console.log(param);
});

addEventListener()fetch 都会大量用到回调函数。回调函数的另一个好处在于代码复用更加的灵活,所以回调函数在很多语言中都有方式。恰好最近在重构一段 Java 代码,索性将我了解的几种语言做一次小结。

C

在 C 语言中回调函数是以函数指针(function pointer)的形式体现的。示例代码中 fun_ptr 指向了函数 foo 的地址,自然可以把 fun_ptr 当作变量传递给另一个函数。

#include <stdio.h>

void foo(int i) {
	printf("the value of input is %d\n", i);
}

int main() {
	void (*fun_ptr)(int) = foo;

	(*fun_ptr)(42);

	return 0;
}

为了简化书写,实际项目中会使用 typedef 来定义函数指针的原型形式。例如:

#include <stdio.h>

void foo(int i) {
	printf("the value of input is %d\n", i);
}

typedef void (*my_handler)(int);

int main() {
	my_handler h = foo;

	h(42);

	return 0;
}

这样函数指针看起来更像是一种类型,避免稍显复杂的语法在使用的时候写错。

C#

C# 中使用的是 delegate 关键字。C# 我很久不用了,在 MSDN 上有很多篇详细的文档,所以这里简单罗列下语法,不展开讨论。

public delegate void my_handler(int i);

public static void foo(int i) {
	System.Console.WriteLine("the value of input is {0}", i);
}

my_handler h = foo;

h(42);

对比可以看到 C# 的使用和 C 的函数指针比较相似,且类型声明更为简洁。

Java

好了,回到项目中使用的 Java 了。曾经我对语法糖(syntactic sugar)不以为然,有固然好,没有也无所谓,直到最近…真香 :-)

近年初给另一个项目组提供的库里的方式是定义了一个 interface,把它作为函数的参数,示例如下:

interface IMyCallback {
	void do(int i);
}

class Foo {
	public static void foo(int i, IMyCallback cb) {
		cb.do(i);
	}
}

class MyCallback implements IMyCallback {
	@Override
	public void do(int i) {
		System.out.println("the value of input is " + i);
	}

	public static void main(String[] args) {
		MyCallback cb = new MyCallback();

		Foo.foo(42, cb);
	}
}

Stackoverflow 上的这个回答 使用了 generic methods,固然更为灵活… 可是语法上还是稍显复杂了一点。

当时的库是在 Java 7 中编写的。最近的项目使用了 Java 8,所以可以借用 Lambda 表达式稍作简化,接着上段代码:

class MyCallbackUsingLambda {
	public static void main(String[] args) {
		Foo.foo(42, (int i) -> {
			System.out.println("the value of input is " + i);
		});
	}
}

当然 Java 8 中这个 Lambda 表达式写法的回调函数使用也是有点限制的,声名的时候传入的接口(或者类)只能有一个方法,自然这个方法的名字无所谓了。所以这个改进加上 Generic methods 也许会更加灵活易用(?),也许会很混乱,这一点我还没有深入使用,也许受到先入为主的影响,仅从语法层面考虑(也就是忽略底层的实现以及运行效率),C 的函数指针、C# 的 delegate 关键字,或者 JavaScript 的函数对于我来说还是更友好、明确一些。

本文基于署名 2.5 中国大陆许可协议发布,欢迎转载,演绎或用于商业目的,但是必须保留本文的署名 IronBlood(包含链接)。