这可能是常见且微不足道的事情,但我似乎很难找到具体的答案。在 C# 中有一个委托的概念,它与 C++ 中的函数指针的概念密切相关。Java中是否有类似的功能?鉴于指针有些缺席,最好的方法是什么?需要明确的是,我们在这里谈论的是头等舱。
11 回答
类似函数指针的功能的 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);
您可以用接口替换函数指针。假设你想遍历一个集合并对每个元素做一些事情。
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++;
}
});
你可以使用反射来做到这一点。
将对象和方法名称(作为字符串)作为参数传递,然后调用该方法。例如:
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");
当然,检查所有异常并添加所需的演员表。
不,函数不是 java 中的第一类对象。您可以通过实现处理程序类来做同样的事情 - 这就是在 Swing 等中实现回调的方式。
然而,在 Java 的未来版本中存在关于闭包(您正在谈论的正式名称)的建议 - Javaworld有一篇有趣的文章。
这让人想起史蒂夫·耶格在名词王国的处决。它基本上表明 Java 需要一个对象来执行每个操作,因此没有像函数指针这样的“仅动词”实体。
要实现类似的功能,您可以使用匿名内部类。
如果你要定义一个接口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.
}
}
Java8 引入了 lambda 和方法引用。因此,如果您的函数与功能接口匹配(您可以创建自己的),则可以在这种情况下使用方法引用。
Java 提供了一组通用的功能接口。而您可以执行以下操作:
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
}
}
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");
}
}
}
我已经使用反射在 Java 中实现了回调/委托支持。详细信息和工作源可在我的网站上找到。
这个怎么运作
我们有一个名为 Callback 的原理类和一个名为 WithParms 的嵌套类。需要回调的 API 将 Callback 对象作为参数,如果需要,创建一个 Callback.WithParms 作为方法变量。由于该对象的许多应用程序将是递归的,因此它非常干净。
由于性能对我来说仍然是一个高优先级,我不想被要求创建一个一次性对象数组来保存每次调用的参数 - 毕竟在大型数据结构中可能有数千个元素,并且在消息处理中在这种情况下,我们最终可能每秒处理数千个数据结构。
为了线程安全,每次调用 API 方法的参数数组都需要唯一存在,并且为了提高效率,每次调用回调都应该使用相同的数组;我需要第二个创建成本低廉的对象,以便将回调与参数数组绑定以进行调用。但是,在某些情况下,由于其他原因,调用者可能已经拥有一个参数数组。由于这两个原因,参数数组不属于回调对象。此外,调用的选择(将参数作为数组或作为单个对象传递)属于 API 的手中,使用回调使其能够使用最适合其内部工作的任何调用。
WithParms 嵌套类是可选的,有两个用途,它包含回调调用所需的参数对象数组,它提供了 10 个重载的 invoke() 方法(具有 1 到 10 个参数),它们加载参数数组,然后调用回调目标。
检查闭包是如何在 lambdaj 库中实现的。它们实际上具有与 C# 委托非常相似的行为:
相对于这里的大多数人,我是 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();