回调函数,就是回头再调用的函数

又遇到了回调函数,这次打算写下来分享一下.水平有限,如有错误,请指正. 转载请注出处.

所谓回调函数,或者在面向对象语言里叫回调方法,简单点讲,就是回头在某个时间(事件发生)被调用的函数.

再详细点:就是一个函数A,作为参数,传入了另一个函数B,然后被B在某个时间调用.

这里可以有疑问了,既然是一个函数调用另一个函数,可以在函数体里面调用啊,为什么还要把函数作为参数传到另一个函数里被调用?何况还有一些语言(比如java)不支持把函数作为参数.

对的,确实可以在函数体里调用另一个函数,功能上好像是没差别的,但是这里有一个问题,就是你要调用的这个函数被写死了,也就是说这样函数B只能调用函数A了,这样如果在另一个情景下,有个与A不同实现的函数C也需要在B的某个时刻被调用,那怎么办.

下面继续说回调函数,在c/c++里,回调函数可以使用函数指针作为参数被另一个函数调用;在c#里,可以使用委托,如果是事件方法的话,还有event关键字;在python和js里,可以直接把函数当对象传参,这些语言都很好实现回调函数(方法),可是, java呢? 先说点题外话,自从学了C#,觉得java真心难用,曾经一度打算以后不再用java,可是现实并没有那么理想,我现在要做android,所以还是不能放下java,而且今天遇到这个回调函数的问题,也是从java里遇到的,我个人觉得,在这个博客里出现的语言,除了java外,对于回调,都可以既容易,又好理解的实现,但是java,我觉得并不是那样,不然我也不会来写这篇博客.

好了继续说,关于java中的回调方法的实现.这篇博客的重点就是说java的. 在java中,回调方法是用借用接口来实现的,我在网上找到一句话:"把实现某一接口的类所创建的对象的引用,赋值给该接口声明的接口变量,那么该接口变量就可以调用被实现的接口的方法". 很绕哈,简单解释下:

有一个接口,接口里有一个方法(这个方法就是要回调的方法)

    interface CallBackInterface {
        void callBackMethod();
    }

我们知道,接口对象不能直接用,因为里面的方法都没有实现.所以要找个类实现这个接口.

所以现在加一个类,实现这个接口.

    interface CallBackInterface {
        void callBackMethod();
    }

    class CallBackClass implements CallBackInterface{

        @Override
        public void callBackMethod() {
            System.out.println("hello");
        }
    }

好了,最后一步:把实现了接口的类的对象赋值给声明的接口变量(我给写进一个方法里了,然后外面加了个类的壳子):

public class CallBackTest {
	interface CallBackInterface {
		void callBackMethod();
	}

	class CallBackClass implements CallBackInterface {

		@Override
		public void callBackMethod() {
			System.out.println("hello");
		}
	}

	public void showCallBack() {
		CallBackInterface itfs = new CallBackClass();
		itfs.callBackMethod();
	}
}

现在可以调用试试看了:

public class Test {
	public static void main(String[] args) {
		new CallBackTest().showCallBack();
	}
}

没意外的话,会成功输出hello,反正我这边是的.

例子看完了,所以说我做了什么呢? 再详细点说,我们有一个要在某一个方法里被调用的方法(这个方法就是回调方法), 前面我们也说了,最好不要直接把想要回调方法做的事直接写在调用方法里, 又因为java里没法把方法当做参数传递,所以我们只好把这个回调方法放在了接口里(为什么不是类?不是抽象类?而是接口?你可以自己去找下抽象类与接口的异同,自己解决这个问题).有接口的话,就要被类实现,然后,只要是给接口的对象赋予实现类的对象,这个接口的对象就可以调用那个方法了.理解这里的话,有一个重点,就是多态, 这里用到的多态知识就是,接口的对象可以顺利被子类赋值,并且调用子类的重写方法(类也有类似的概念).

再多说一点,这里任何实现了CallbackInterface接口的类,都可以像下面这样放在new后面(就是赋值):

CallBackInterface itfs = new CallBackClass();

这样就把代码抽象了是不是, 反正这里只是要一个实现了CallbackInterface接口的对象, 我才不管是什么类的对象,那类到底怎么实现的,实现里干了什么事.这些统统不需要这里的调用者知道. 我突然想起来某本c#书里说的一句话: 接口就是一个约定. 确实,这里的情况就是,所有遵守这个约定的类,都可以赋值,被调用.在java里,这个约定只能是接口,而C#里,可以是接口,也可以是委托,在c/c++里,就是函数指针,在python和js,就是一个对象了.

好了差不多就说这些了,最后补一个代码,是上面的修改版,上面的例子只是为了讲清楚那句红色的话,下面的例子应该才是常用到的情况, 也就是在另一个类的某个方法里在某个事件发生时调用回调方法.(其实回调的话,大多用在事件处理里面,在android代码里,经常会用到on*****. 最后的最后, 看看下面的代码,和c#的event多像啊,可是c#的代码比这个简单多了)

public class CallBackTest {
	interface CallBackInterface {
		void callBackMethod();
	}

	class CallBackClass implements CallBackInterface {

		@Override
		public void callBackMethod() {
			System.out.println("hello");
		}
	}

	class Controller {
		private CallBackInterface cbitf;
		// 这个boolean只是为了模拟有事件,没啥实用价值
		public boolean somethingHappend;
		// 这里确实可以直接把CallBackClass做参数,而且省掉接口的定义
		// 但是这样做的话,就像是回调函数直接写在了调用函数里一样
		// 不明白的话就好好理解下"约定"和"调用者不管回调函数是怎么实现的"吧
		public Controller(CallBackInterface itfs) {
			somethingHappend = true;
			this.cbitf = itfs;
		}

		public void doSomething() {
			if(somethingHappend) {
				cbitf.callBackMethod();
			}
		}
	}

	public void showCallBack() {
		CallBackClass cbc = new CallBackClass();
		Controller ctrlr = new Controller(cbc);
		ctrlr.doSomething();
		// 其实上面也可以这样写在一行里
		// new Controller(new CallBackClass()).doSomething();
	}
}
				
时间: 2024-12-11 04:18:53

回调函数,就是回头再调用的函数的相关文章

Python(74)_编写装饰器,为多个函数加上记录调用功能,要求每次调用函数都将被调用的函数名写入文件

#-*-coding:utf-8-*- import os import time from functools import wraps ''' 1.编写装饰器,为多个函数加上记录调用功能,要求每次调用函数都将被调用的函数名写入文件 ''' def log(func): def inner(*args,**kwargs): with open('log1.txt','a',encoding='utf-8') as f: f.write(func.__name__+'\n') ret = fun

函数内部用setTimeout()调用自身函数相当于setInterval()

本来setTimeout(function(){},time)只执行了一次function,但是当 function demo() { alert(1); setTimeout('demo()' ,500);} 此时每隔0.5秒就会alert,但是我就觉得奇怪,因为setTimeout()本来就是只执行一次而已,为什么会一直不断的出现呢?感觉好像setInterval(): 后来才发现原来他是调用了自身的函数,所以是一直嵌套自身函数,才会这样一直循环. 如果改成这样, function demo

编写一个求字符串长度的函数strlen(),再用strlen()函数编写一个函数reverse(s)的倒序递归程序,使字符串s逆序-简单

源程序: #include < iostream > #include < string > using namespace std; int strlen(char *str) { int len = 0; while (str[len] != '\0') { len++; } return len; } void revers(char *b) { char c; int j, len; len = strlen(b); j = len / 2 - 1; while (j &g

Shell函数返回值、删除函数、在终端调用函数

Shell 也支持函数.Shell 函数必须先定义后使用. Shell 函数的定义格式如下: function_name () { list of commands [ return value ] } 如果你愿意,也可以在函数名前加上关键字 function: function function_name () { list of commands [ return value ] } 函数返回值,可以显式增加return语句:如果不加,会将最后一条命令运行结果作为返回值. Shell 函数返

Shell函数:Shell函数返回值、删除函数、在终端调用函数

函数可以让我们将一个复杂功能划分成若干模块,让程序结构更加清晰,代码重复利用率更高.像其他编程语言一样,Shell 也支持函数.Shell 函数必须先定义后使用. Shell 函数的定义格式如下: function_name () { list of commands [ return value ] } 如果你愿意,也可以在函数名前加上关键字 function: function function_name () { list of commands [ return value ] } 函数

【Shell脚本学习22】Shell 函数:Shell函数返回值、删除函数、在终端调用函数

函数可以让我们将一个复杂功能划分成若干模块,让程序结构更加清晰,代码重复利用率更高.像其他编程语言一样,Shell 也支持函数.Shell 函数必须先定义后使用. Shell 函数的定义格式如下: function_name () { list of commands [ return value ] } 如果你愿意,也可以在函数名前加上关键字 function: function function_name () { list of commands [ return value ] } 函数

C++面试题1:构造函数和虚构函数中能否调用虚函数?

C++面试题1:构造函数和虚构函数中能否调用虚函数? 构造函数跟虚构函数里面都可以调用虚函数,编译器不会报错. C++ primer中说到最好别用 由于类的构造次序是由基类到派生类,所以在构造函数中调用虚函数,虚函数是不会呈现出多态的 类的析构是从派生类到基类,当调用继承层次中某一层次的类的析构函数时意味着其派生类部分已经析构掉,所以也不会呈现多态 因此如果在基类中声明的纯虚函数并且在基类的析构函数中调用之,编译器会发生错误. class Base { public: Base() { Fuct

linux平台学x86汇编(十九):C语言中调用汇编函数

[版权声明:尊重原创,转载请保留出处:blog.csdn.net/shallnet,文章仅供学习交流,请勿用于商业用途] 除了内联汇编以外,还有一种途径可以把汇编代码整合到C/C++语言中,C/C++语言可以直接调用汇编函数,把输入值传递给函数,然后从函数获得输出值. 如果希望汇编语言函数和C/C++程序一起工作,就必须显示地遵守C样式的函数格式,也就是说所有输入变量都必须从堆栈读取,并且大多数输入值都返回到EAX嫁寄存器中.在汇编函数代码中,C样式函数对于可以修改哪些寄存器和函数必须保留哪些寄

matlab 函数的编写与调用

matlab中写个函数,在主程序中调用该函数的方法 跟其它的编程语言都一样,但是子函数与主函数要存于不同的文件中,文件名就是函数名字.文件必须保存在current directory中,才能调用. 函数的基本结构: function [返回变量列表]=函数名(输入变量列表) %注释说明语句 输入.返回变量格式的检测 函数体 %--------简例----- 主函数 main.my=test(x)子函数 test.m (文件名字即为函数名字)function z=test(x)z=x.*x; %-