如何用简单的英语解释回调?它们与从一个函数调用另一个函数从调用函数获取上下文有什么不同?如何向新手程序员解释它们的强大功能?


当前回答

我要尽量让这个问题简单化。“回调”是由另一个以第一个函数为参数的函数调用的任何函数。很多时候,“回调”是在发生某些事情时调用的函数。在编程语言中,这可以被称为“事件”。

想象一下这样的场景:你几天后就会收到一个包裹。这个包裹是给邻居的礼物。因此,一旦你得到了包裹,你想把它带到邻居那里。你出城了,所以你给你的配偶留下了指示。

你可以让他们去拿包裹,然后送到邻居那里。如果你的配偶像电脑一样愚蠢,他们会坐在门口等包裹来(什么也不做),然后一旦它来了,他们就会把它带到邻居那里。但是有一个更好的办法。告诉你的配偶,一旦他们收到包裹,他们应该把它带到邻居那里。然后,他们可以正常生活,直到他们收到包裹。

在我们的例子中,包的接收是“事件”,而把它带给邻居是“回调”。你的配偶“执行”你的指示,只有当包裹到达时才把包裹带过来。更好的!

这种思维在日常生活中很明显,但计算机没有这种常识。考虑程序员通常如何写入文件:

fileObject = open(file)
# now that we have WAITED for the file to open, we can write to it
fileObject.write("We are writing to the file.")
# now we can continue doing the other, totally unrelated things our program does

在这里,我们等待文件打开,然后再写入它。这“阻塞”了执行流,我们的程序不能做它可能需要做的任何其他事情!如果我们可以这样做:

# we pass writeToFile (A CALLBACK FUNCTION!) to the open function
fileObject = open(file, writeToFile)
# execution continues flowing -- we don't wait for the file to be opened
# ONCE the file is opened we write to it, but while we wait WE CAN DO OTHER THINGS!

事实证明,我们可以通过一些语言和框架来实现这一点。太酷了!查看Node.js来获得一些这种思考的实际实践。

其他回答

通常,我们将变量发送给函数:function1(var1, var2)。

假设,你想在它被作为参数给出之前处理它:

这是一种回调类型,其中function2执行一些代码并将变量返回给初始函数。

编辑:回调这个词最常见的意思是一个函数作为参数传递给另一个函数,并在稍后的时间点被调用。这些思想存在于允许高阶函数的语言中,即将函数视为一等公民,通常用于异步编程中。onready dosomething()。在这里,只有当它准备好了,事情才会发生。

A callback is a function that will be called by a second function. This second function doesn't know in advance what function it will call. So the identity of the callback function is stored somewhere, or passed to the second function as a parameter. This "identity," depending on the programming language, might be the address of the callback, or some other sort of pointer, or it might be the name of the function. The principal is the same, we store or pass some information that unambiguously identifies the function.

当时间到来时,第二个函数可以调用回调,根据当时的情况提供参数。它甚至可以从一组可能的回调中选择回调。编程语言必须提供某种语法,允许第二个函数调用回调函数,并知道它的“标识”。

这种机制有许多可能的用途。通过回调,函数的设计者可以通过调用所提供的任何回调来定制函数。例如,排序函数可能将回调函数作为参数,而这个回调函数可能是用于比较两个元素以决定哪个元素先出现的函数。

顺便说一下,根据编程语言的不同,上面讨论中的“函数”一词可能会被“块”、“闭包”、“lambda”等取代。

简单明了:回调是你给另一个函数的函数,这样它就可以调用它。

通常在某个操作完成时调用它。由于在将回调函数交给其他函数之前创建了回调,因此可以使用调用站点的上下文信息初始化它。这就是为什么它被命名为call*back* -第一个函数从它被调用的地方回调到上下文。

如果没有回调或其他特殊的编程资源(如线程等),程序就是一个指令序列,一个接一个地按顺序执行,即使具有某种由某些条件决定的“动态行为”,所有可能的场景都必须预先编程。

因此,如果我们需要为程序提供一个真正的动态行为,我们可以使用回调。通过回调,你可以通过参数指令,一个程序调用另一个程序,提供一些先前定义的参数,并可以期望一些结果(这是契约或操作签名),因此这些结果可以由之前不知道的第三方程序产生/处理。

这种技术是应用于计算机运行的程序、函数、对象和所有其他类型代码的多态的基础。

以回调为例的人类世界很好地解释了当你在做一些工作时,假设你是一个画家(这里你是主程序,负责绘画),有时会打电话给你的客户,请他批准你的工作结果,所以,他决定图片是否好(你的客户是第三方程序)。

在上面的例子中,你是一个画家,并“委托”给其他人的工作来批准结果,图片是参数,每个新的客户端(回调的“函数”)改变你的工作结果,决定他想要的图片(客户端做出的决定是“回调函数”返回的结果)。

我希望这个解释对你有用。

在计算机编程中,回调是对可执行代码或一段可执行代码的引用,它作为参数传递给其他代码。这允许较低级别的软件层调用较高级别的软件层定义的子例程(或函数)。——维基百科

在C语言中使用函数指针进行回调

在C语言中,回调是使用函数指针实现的。函数指针——顾名思义,是一个指向函数的指针。

例如,int (*ptrFunc) ();

这里,ptrFunc是一个指向不带参数并返回整数的函数的指针。不要忘记加上圆括号,否则编译器会认为ptrFunc是一个普通的函数名,它不接受任何参数,只返回一个指向整数的指针。

下面是演示函数指针的一些代码。

#include<stdio.h>
int func(int, int);
int main(void)
{
    int result1,result2;
    /* declaring a pointer to a function which takes
       two int arguments and returns an integer as result */
    int (*ptrFunc)(int,int);

    /* assigning ptrFunc to func's address */                    
    ptrFunc=func;

    /* calling func() through explicit dereference */
    result1 = (*ptrFunc)(10,20);

    /* calling func() through implicit dereference */        
    result2 = ptrFunc(10,20);            
    printf("result1 = %d result2 = %d\n",result1,result2);
    return 0;
}

int func(int x, int y)
{
    return x+y;
}

现在让我们尝试理解C语言中使用函数指针的回调概念。

完整的程序有三个文件:callback.c, reg_callback.h和reg_callback.c。

/* callback.c */
#include<stdio.h>
#include"reg_callback.h"

/* callback function definition goes here */
void my_callback(void)
{
    printf("inside my_callback\n");
}

int main(void)
{
    /* initialize function pointer to
    my_callback */
    callback ptr_my_callback=my_callback;                        
    printf("This is a program demonstrating function callback\n");
    /* register our callback function */
    register_callback(ptr_my_callback);                          
    printf("back inside main program\n");
    return 0;
}

/* reg_callback.h */
typedef void (*callback)(void);
void register_callback(callback ptr_reg_callback);


/* reg_callback.c */
#include<stdio.h>
#include"reg_callback.h"

/* registration goes here */
void register_callback(callback ptr_reg_callback)
{
    printf("inside register_callback\n");
    /* calling our callback function my_callback */
    (*ptr_reg_callback)();                               
}

如果我们运行这个程序,输出将是

这是一个演示函数回调的程序 内部register_callback 内部my_callback 回到主程序

上层函数像正常调用一样调用下层函数,而回调机制允许下层函数通过指向回调函数的指针调用上层函数。

Java中使用接口的回调

Java没有函数指针的概念 它通过接口机制实现回调机制 在这里,我们声明了一个接口,而不是函数指针,它有一个方法,当被调用方完成其任务时将被调用

让我通过一个例子来说明:

回调接口

public interface Callback
{
    public void notify(Result result);
}

调用者或更高级别的类

public Class Caller implements Callback
{
Callee ce = new Callee(this); //pass self to the callee

//Other functionality
//Call the Asynctask
ce.doAsynctask();

public void notify(Result result){
//Got the result after the callee has finished the task
//Can do whatever i want with the result
}
}

被调用者或底层函数

public Class Callee {
Callback cb;
Callee(Callback cb){
this.cb = cb;
}

doAsynctask(){
//do the long running task
//get the result
cb.notify(result);//after the task is completed, notify the caller
}
}

使用EventListener模式

列表项

此模式用于通知0到n个观察者/监听器某个特定任务已经完成

列表项

回调机制和EventListener/Observer机制之间的区别在于,在回调中,被调用方通知单个调用方,而在Eventlisener/Observer中,被调用方可以通知任何对该事件感兴趣的人(通知可能会到应用程序中尚未触发任务的其他部分)。

让我通过一个例子来解释。

事件接口

public interface Events {

public void clickEvent();
public void longClickEvent();
}

类部件

package com.som_itsolutions.training.java.exampleeventlistener;

import java.util.ArrayList;
import java.util.Iterator;

public class Widget implements Events{

    ArrayList<OnClickEventListener> mClickEventListener = new ArrayList<OnClickEventListener>(); 
    ArrayList<OnLongClickEventListener> mLongClickEventListener = new ArrayList<OnLongClickEventListener>();

    @Override
    public void clickEvent() {
        // TODO Auto-generated method stub
        Iterator<OnClickEventListener> it = mClickEventListener.iterator();
                while(it.hasNext()){
                    OnClickEventListener li = it.next();
                    li.onClick(this);
                }   
    }
    @Override
    public void longClickEvent() {
        // TODO Auto-generated method stub
        Iterator<OnLongClickEventListener> it = mLongClickEventListener.iterator();
        while(it.hasNext()){
            OnLongClickEventListener li = it.next();
            li.onLongClick(this);
        }

    }

    public interface OnClickEventListener
    {
        public void onClick (Widget source);
    }

    public interface OnLongClickEventListener
    {
        public void onLongClick (Widget source);
    }

    public void setOnClickEventListner(OnClickEventListener li){
        mClickEventListener.add(li);
    }
    public void setOnLongClickEventListner(OnLongClickEventListener li){
        mLongClickEventListener.add(li);
    }
}

类按钮

public class Button extends Widget{
private String mButtonText;
public Button (){
} 
public String getButtonText() {
return mButtonText;
}
public void setButtonText(String buttonText) {
this.mButtonText = buttonText;
}
}

类复选框

public class CheckBox extends Widget{
private boolean checked;
public CheckBox() {
checked = false;
}
public boolean isChecked(){
return (checked == true);
}
public void setCheck(boolean checked){
this.checked = checked;
}
}

Activity类

包com.som_itsolutions.training.java.exampleeventlistener;

public class Activity implements Widget.OnClickEventListener
{
    public Button mButton;
    public CheckBox mCheckBox;
    private static Activity mActivityHandler;
    public static Activity getActivityHandle(){
        return mActivityHandler;
    }
    public Activity ()
    {
        mActivityHandler = this;
        mButton = new Button();
        mButton.setOnClickEventListner(this);
        mCheckBox = new CheckBox();
        mCheckBox.setOnClickEventListner(this);
        } 
    public void onClick (Widget source)
    {
        if(source == mButton){
            mButton.setButtonText("Thank you for clicking me...");
            System.out.println(((Button) mButton).getButtonText());
        }
        if(source == mCheckBox){
            if(mCheckBox.isChecked()==false){
                mCheckBox.setCheck(true);
                System.out.println("The checkbox is checked...");
            }
            else{
                mCheckBox.setCheck(false);
                System.out.println("The checkbox is not checked...");
            }       
        }
    }
    public void doSomeWork(Widget source){
        source.clickEvent();
    }   
}

其他类

public class OtherClass implements Widget.OnClickEventListener{
Button mButton;
public OtherClass(){
mButton = Activity.getActivityHandle().mButton;
mButton.setOnClickEventListner(this);//interested in the click event                        //of the button
}
@Override
public void onClick(Widget source) {
if(source == mButton){
System.out.println("Other Class has also received the event notification...");
}
}

主类

public class Main {
public static void main(String[] args) {
// TODO Auto-generated method stub
Activity a = new Activity();
OtherClass o = new OtherClass();
a.doSomeWork(a.mButton);
a.doSomeWork(a.mCheckBox);
}
}

从上面的代码中可以看到,我们有一个名为events的接口,它基本上列出了应用程序可能发生的所有事件。Widget类是所有UI组件(如按钮、复选框)的基类。这些UI组件是实际从框架代码接收事件的对象。Widget类实现了Events接口,它也有两个嵌套接口,即OnClickEventListener和OnLongClickEventListener

These two interfaces are responsible for listening to events that may occur on the Widget derived UI components like Button or Checkbox. So if we compare this example with the earlier Callback example using Java Interface, these two interfaces work as the Callback interface. So the higher level code (Here Activity) implements these two interfaces. And whenever an event occurs to a widget, the higher level code (or the method of these interfaces implemented in the higher level code, which is here Activity) will be called.

Now let me discuss the basic difference between Callback and Eventlistener pattern. As we have mentioned that using Callback, the Callee can notify only a single Caller. But in the case of EventListener pattern, any other part or class of the Application can register for the events that may occur on the Button or Checkbox. The example of this kind of class is the OtherClass. If you see the code of the OtherClass, you will find that it has registered itself as a listener to the ClickEvent that may occur in the Button defined in the Activity. Interesting part is that, besides the Activity ( the Caller), this OtherClass will also be notified whenever the click event occurs on the Button.