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