2010-10-28 52 views
4

我遇到了有关Java中的回调的question。她的跑步code和原始答案here在Java中回调(代码解释)

  1. 但我不明白它是如何有用的回调?
  2. 你能解释一下Java程序员回调的概念吗?

代码:

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

我写了一个小博客文章在这一段时间回:http://madhurtanwani.blogspot.com/2010/09/callbacks-in-java.html。希望能帮助到你!

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

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

  1. 的main()函数实际上是一个数(读取数据)流解析算法,其将解析一对数字(数据集)然后对它们进行操作。
  2. 现在,考虑到问题的分离,main()函数不应该知道或理解可以在数据集上执行哪些操作。实际上,它甚至不应该关心数据集的数据类型。
  3. 但是,由于这些数据集是特定于您的域的,所以主要功能应该是委托将数据处理到您的域特定类。
  4. 为了做到这一点,它必须与域特定的调用者(主调用者)强制执行合同并说 - 看,我会在每次收到一对数据时调用doJob方法的Visitorinterface实现集。调用者必须做的是实现Visitor接口并实现特定于领域的逻辑来处理数据集。

呼叫者委派处理,回被叫的部分称为回调使用的Java interface(合同规范)来实现。

+0

有趣的阅读,但我也试图了解发布的代码。 – zengr 2010-10-28 09:41:24

0

你在谈论一个我从来没有称自己为回叫的实体。你正在谈论的实体称为函数指针(如madhuranwani提到的)/代表/匿名函数/动作。

这些通常用于定制通用算法实现(如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(); 
} 
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; 
    } 
}; 
... 

他们是事件处理(认为秋千),你通常需要实现,你需要有一个actionPerormed()方法ActionListener接口特别有用。 如果你有一个包含许多不同控件的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 
}); 
+0

根据你的评论,它的实际做法是:创建实现该接口的类并执行它们。类似的(但不完全相同,请参阅后)有一个Visitor接口和2个“命名”,接口的“具体”实现,可以称它们为“MultiplierVisitor实现Visitor”和“AdderVisitor实现Visistor” – iggymoran 2010-10-28 11:12:56

3

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

但Java没有函数指针,因此,Java程序员需要使用匿名内部类接口之类的东西,以封装一个类中的功能,并通过一个该类的实例作为回调。

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

3

我曾经在Java中开发过,直到我开始用C#及其代表编程时,我才完全理解回调概念。

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

Java中的回调概念可以通过使用接口并将它们(实现它们的对象)作为参数传递来实现。想象一下,你有以下接口定义了2个方法,任何想要作为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以使用它的方法divideAndNotify,该方法接受2个数字并引用将要侦听结果的实例(在我们的情况下,这种情况下是MyMainScreen实例本身,这就是为什么它通过自身字

件SmartClass方法divideAndNotify知道数学,并会通知谁与操作的结果听。它的方法知道resultListener将包含对结果成功或失败时知道该做什么的对象的引用。

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

作为一个总结:A 回调只是一个任务的代表团。 PS:使用C#这个概念更直接,因为C#具有委托类型,这些委托类型是存储函数所在的内存地址的变量(存储的函数必须与委托中定义的签名相匹配)。