Java中的函数指针

时间:2009-07-02 09:19:44

标签: java pointers delegates function-pointers

这可能是常见和微不足道的事情,但我似乎无法找到具体的答案。在C#中有一个委托的概念,它与C ++中的函数指针的思想密切相关。 Java中是否有类似的功能?鉴于指针有点缺席,最好的方法是什么?很明显,我们在这里谈论头等舱。

11 个答案:

答案 0 :(得分:122)

类似于函数指针的功能的Java习惯用法是一个实现接口的匿名类,例如

Collections.sort(list, new Comparator<MyClass>(){
    public int compare(MyClass a, MyClass b)
    {
        // compare objects
    }
});

更新:以上在Java 8之前的Java版本中是必要的。现在我们有更好的选择,即lambdas:

list.sort((a, b) -> a.isGreaterThan(b));

和方法参考:

list.sort(MyClass::isGreaterThan);

答案 1 :(得分:63)

您可以使用接口替换函数指针。假设你想要运行一个集合并对每个元素做一些事情。

public interface IFunction {
  public void execute(Object o);
}

这是我们可以传递给CollectionUtils2.doFunc(Collection c,IFunction f)的接口。

public static void doFunc(Collection c, IFunction f) {
   for (Object o : c) {
      f.execute(o);
   }
}

举个例子说我们有一组数字,你想为每个元素加1。

CollectionUtils2.doFunc(List numbers, new IFunction() {
    public void execute(Object o) {
       Integer anInt = (Integer) o;
       anInt++;
    }
});

答案 2 :(得分:41)

您可以使用反射来完成它。

将对象和方法名称(作为字符串)作为参数传递,然后调用该方法。例如:

Object methodCaller(Object theObject, String methodName) {
   return theObject.getClass().getMethod(methodName).invoke(theObject);
   // Catch the exceptions
}

然后按照以下方式使用它:

String theDescription = methodCaller(object1, "toString");
Class theClass = methodCaller(object2, "getClass");

当然,检查所有异常并添加所需的强制转换。

答案 3 :(得分:20)

不,函数不是java中的第一类对象。你可以通过实现一个处理程序类来做同样的事情 - 这就是在Swing等中实现回调的方式。

但是在未来版本的java中,有关闭包的提议(你正在谈论的官方名称) - Javaworld有一篇有趣的文章。

答案 4 :(得分:13)

这让人想起Steve Yegge的Execution in the Kingdom of Nouns。它基本上表明Java需要每个动作都有一个对象,因此没有像函数指针那样的“仅动词”实体。

答案 5 :(得分:7)

要实现类似功能,您可以使用匿名内部类。

如果您要定义界面Foo

interface Foo {
    Object myFunc(Object arg);
}

创建一个方法bar,它将接收一个'函数指针'作为参数:

public void bar(Foo foo) {
    // .....
    Object object = foo.myFunc(argValue);
    // .....
}

最后按如下方式调用方法:

bar(new Foo() {
    public Object myFunc(Object arg) {
        // Function code.
    }
}

答案 6 :(得分:5)

Java中没有这样的东西。您需要将函数包装到某个对象中,并将引用传递给该对象,以便将引用传递给该对象上的方法。

从语法上讲,通过使用定义为类的成员变量的就地定义的匿名类或匿名类,可以在一定程度上缓解这种情况。

示例:

class MyComponent extends JPanel {
    private JButton button;
    public MyComponent() {
        button = new JButton("click me");
        button.addActionListener(buttonAction);
        add(button);
    }

    private ActionListener buttonAction = new ActionListener() {
        public void actionPerformed(ActionEvent e) {
            // handle the event...
            // note how the handler instance can access 
            // members of the surrounding class
            button.setText("you clicked me");
        }
    }
}

答案 7 :(得分:5)

Java8引入了lambdas和method references。因此,如果您的函数与functional interface匹配(您可以创建自己的函数),则可以在这种情况下使用方法引用。

Java提供了一组common functional interfaces。而你可以做到以下几点:

public class Test {
   public void test1(Integer i) {}
   public void test2(Integer i) {}
   public void consumer(Consumer<Integer> a) {
     a.accept(10);
   }
   public void provideConsumer() {
     consumer(this::test1);   // method reference
     consumer(x -> test2(x)); // lambda
   }
}

答案 8 :(得分:3)

我使用反射在Java中实现了回调/委托支持。详细信息和工作来源为available on my website

如何运作

我们有一个名为Callback的原则类,它有一个名为WithParms的嵌套类。需要回调的API将Callback对象作为参数,如果需要,创建一个Callback.WithParms作为方法变量。由于此对象的大量应用程序将是递归的,因此非常干净。

由于性能仍然是我的首要任务,我不希望被要求创建一个一次性对象数组来保存每个调用的参数 - 毕竟在大型数据结构中可能有数千个元素,并且在消息处理场景中,我们最终可能会在一秒钟内处理数千个数据结构。

为了成为线程安全,参数数组需要为API方法的每次调用唯一存在,为了提高效率,每次调用回调都应使用相同的参数;我需要第二个对象,它可以很便宜地创建,以便将回调与参数数组绑定以进行调用。但是,在某些情况下,由于其他原因,调用者已经拥有了参数数组。由于这两个原因,参数数组不属于Callback对象。此外,调用的选择(将参数作为数组或单个对象传递)属于API的手中,使用回调使其能够使用最适合其内部工作的调用。

然后,WithParms嵌套类是可选的,有两个用途,它包含回调调用所需的参数对象数组,它提供10个重载的invoke()方法(带有1到10个参数),用于加载参数然后调用数组然后调用回调目标。

答案 9 :(得分:2)

检查闭包是如何在lambdaj库中实现它们的。它们实际上的行为与C#委托非常相似:

http://code.google.com/p/lambdaj/wiki/Closures

答案 10 :(得分:0)

相对于大多数人来说,我是java的新手,但由于我还没有看到类似的建议,我还有另一种建议。我不确定它是否是一个好的做法,甚至以前建议,我只是没有得到它。我喜欢它,因为我认为它是自描述的。

 /*Just to merge functions in a common name*/
 public class CustomFunction{ 
 public CustomFunction(){}
 }

 /*Actual functions*/
 public class Function1 extends CustomFunction{
 public Function1(){}
 public void execute(){...something here...}
 }

 public class Function2 extends CustomFunction{
 public Function2(){}
 public void execute(){...something here...}
 }

 .....
 /*in Main class*/
 CustomFunction functionpointer = null;

然后根据应用程序,分配

 functionpointer = new Function1();
 functionpointer = new Function2();

等。

并通过

致电
 functionpointer.execute();