用Java回调(代码说明)

时间:2010-10-28 09:33:06

标签: java callback

我遇到了关于Java回调的question。 Hers是正在运行的code和原始答案here

  1. 但我不明白它对回调有什么用处?
  2. 你能解释回调给Java程序员的概念吗?
  3. 代码:

    public class Main {
    
        public interface Visitor {
            int DoJob(int a, int b);
        }
    
    
        public static void main(String[] args) {
            Visitor adder = new Visitor(){
                public int DoJob(int a, int b) {
                    return a + b;
                }
            };
    
            Visitor multiplier = new Visitor(){
                public int DoJob(int a, int b) {
                    return a*b;
                }
            };
    
            System.out.println(adder.DoJob(10, 20));
            System.out.println(multiplier.DoJob(10, 20));
    
        }
    }
    

5 个答案:

答案 0 :(得分:6)

我曾在这篇文章中写过一篇小博文:http://madhurtanwani.blogspot.com/2010/09/callbacks-in-java.html。希望它有所帮助!

在我尝试解释上面的代码之前,我必须说,它不是最直观或最好的回调。我在帖子中使用的示例是Collections.sort(),它清楚地显示了回调部分。

Neverthelss,对于上面发布的代码,想到这样:

  1. main()函数实际上是一个数字(读取数据)流解析算法,它将解析一对数字(数据集),然后对它们进行操作。
  2. 现在,考虑到关注点的分离,main()函数既不知道也不了解可以对数据集执行哪些操作。实际上它甚至不应该关心数据集的数据类型。
  3. 但是,由于这些数据集特定于您的域,因此主要功能应理想委派将数据处理到您的域特定类。
  4. 为此,它必须与特定于域的呼叫者(主要呼叫者)签订合同,然后说 - 看,我会在doJobVisitor { 1}}实现,每当我收到一对数据集。调用者必须做的是实现Visitor接口并实现特定于域的逻辑来处理数据集。
  5. 委派处理从来电回到被叫的部分称为回调,使用 Java中的interface(合同规范)。

答案 1 :(得分:4)

我不想在这里开始一场火焰战争...但是回调的概念在C / C ++,JavaScript,Python,可能是Ruby等许多语言中更容易理解其他。在这些语言中,回调只是一个函数指针。您将函数作为此函数指针传递,其他代码将使用该指针回调函数。就如此容易。 (look at this C example from Wikipedia

但Java没有函数指针,因此Java程序员需要使用匿名内部类接口等类似的东西来封装一个函数一个类,并将该类的实例作为回调传递。

我想我已经设法回答了你的第二个问题(“你能解释回调给Java程序员的概念吗?”),但请参阅其他答案,了解如何在Java中实现它。

答案 2 :(得分:3)

我曾经用Java开发过,直到我开始使用C#及其代表进行编程之前,我还没有完全理解回调概念。

原因是因为@DenilsonSá完全提到Java不使用函数指针。 换句话说,在Java中你可以调用一个方法并传递一些参数,比如原始值(int,long,char,boolean等)和对象(String或任何类的任何实例,就像你传递一个对象一样,你基本上将内存中的地址传递给存在于内存中的真实对象。)

Java中的回调概念可以通过使用接口并将它们(实现它们的对象)作为参数传递来实现。假设您有以下接口,该接口定义了两个希望表现为ResultListener的类必须实现的方法。

interface ResultListener {
   void onSuccessOperation(String description);
   void onFailedOperation(String description);
}

现在假设你有一个在 showScreen 方法中运行的主程序

class MyMainScreen implements ResultListener {
   public void showScreen() {
    //do some things..
    SmartClass smartClass = new SmartClass();
    smartClass.divideAndNotify(5, 0, this);
    }

    public void onSuccessOperation(String description) {
       System.out.println("SUCCESS!!. " + description);
    }

    public void onFailedOperation(String description) {
       System.out.println("FAILED. " + description);
    }
}

这就是知道如何划分的SmartClass。

class SmartClass {
   public void divideAndNotify(int numerador, int denominador, ResultListener resultListener) {
        if (denominador == 0) {
            resultListener.onFailedOperation("Nobody can divide by zero!!");
        } else {
            int total = numerador / denominador;
            resultListener.onSuccessOperation("The result is " + total);
        }
    }
}    

这里有趣的部分是MyMainScreen表现为ResultListener,因此它需要实现接口ResultListener中定义的方法。 MyMainScreen知道如何在控制台上打印消息,但它对计算一无所知,这就是为什么它实例化SmartClass以使用它接受2个数字的方法divideAndNotify以及对将监听结果的实例的引用(在我们的case这个实例是MyMainScreen实例本身,这就是为什么它传递自己的单词 this

SmartClass方法divideAndNotify知道数学,并将通知操作结果的任何人。它的方法知道resultListener将包含对一个对象的引用,该对象知道在结果成功或不成功时该怎么做。

这里的回调概念是SmartClass委派关于如何处理结果的功能,就像在实例中“回调”它作为参数接收的东西。

总结:回调只是一个任务的代表。

PS:使用C#这个概念要简单得多,因为C#有委托类型,这些变量存储函数所在的内存地址(存储的函数必须与委托中定义的签名相匹配)。

答案 3 :(得分:1)

他们被称为“匿名内部类”。它们基本上是接口/抽象类的实现,而不必编写完全成熟的类。

编译器实际上会为每一个创建一个类,所以如果你编译上面的代码,你会看到类似的东西:

Main.class
Main$Visitor.class
Main$1.class  <-- This is probably the "adder" implementation
Main$2.class  <-- This is probably the "multiplier" implementation

这些类的优点在于它们可以从您的方法/类中读取内容,而无需传递这些参数。例如:

...
final int extraMultiplyer = 10;

Visitor multiplier = new Visitor(){
    public int DoJob(int a, int b) {
        //Note that extraMultiplyer is defined in the main() method
        //and is final.
        return a*b*extraMultiplyer;
    }
};
...

它们对于事件处理特别有用(想想Swing),在这里你通常需要实现ActionListener接口,你需要有一个actionPerormed()方法。 如果你有一个包含许多不同控件的UI类,那么每当你注册监听器时,你可能希望将这些匿名类注册到:a)使代码(可以说)更具可读性; b)让每个组件更容易知道而不是有一个巨大的“if-else if-else”风格actionPerfomed实现

例如:

//abbreviated code
button1.addActionListener(new ActionListener(){
    //you do what you need here for the action of pressing this button
});
button2.addActionListener(new ActionListener() {
   //you do what you need here for the action of pressing this button
});

答案 4 :(得分:0)

你说的是我自己从未称之为回调的实体。您正在谈论的实体称为函数指针(如madhuranwani所述)/ delegates / anonymous functions / actions。

这些通常用于定制通用算法实现(例如Collections.sort,正如您所提到的)。

    public class Actions {
    public static void main(String[] args) {
        printingAlgorithm(new Action() {

            public void perform() {
                System.out.println("CustomAction.perform");
            }
        });
    }

    private static void printingAlgorithm(Action customization) {
        System.out.println("------");
        customization.perform();
        System.out.println("++++++");
    }
}

interface Action {
    void perform();
}

在我的区域通常称为回调的实体更像是听众。这是一个例子:

public class Callbacks {

    public static void main(String[] args) {
        printingAlgorithm(new PrintingCallback() {
            public void printBody() {
                System.out.println("custom body");
            }

            public void printHeader() {
                System.out.println("---------");
            }

            public void printFooter() {
                System.out.println("+++++++++");
            }
        });
    }

    private static void printingAlgorithm(PrintingCallback callback) {
        callback.printHeader();
        callback.printBody();
        callback.printFooter();
    }
}

interface PrintingCallback {
    void printHeader();

    void printBody();

    void printFooter();
}