我有一个方法,大约有十行代码。我想创建更多的方法来做完全相同的事情,除了一个会改变一行代码的小计算。这是一个传递函数指针来替换这一行的完美应用程序,但是Java没有函数指针。我最好的选择是什么?


当前回答

你也可以这样做(在一些罕见的情况下是有意义的)。问题(这是一个大问题)是您失去了使用类/接口的所有类型安全性,并且您必须处理方法不存在的情况。

它确实有一个“好处”,即您可以忽略访问限制并调用私有方法(示例中没有显示,但您可以调用编译器通常不允许您调用的方法)。

同样,这在很少的情况下是有意义的,但在那些情况下,这是一个很好的工具。

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

class Main
{
    public static void main(final String[] argv)
        throws NoSuchMethodException,
               IllegalAccessException,
               IllegalArgumentException,
               InvocationTargetException
    {
        final String methodName;
        final Method method;
        final Main   main;

        main = new Main();

        if(argv.length == 0)
        {
            methodName = "foo";
        }
        else
        {
            methodName = "bar";
        }

        method = Main.class.getDeclaredMethod(methodName, int.class);

        main.car(method, 42);
    }

    private void foo(final int x)
    {
        System.out.println("foo: " + x);
    }

    private void bar(final int x)
    {
        System.out.println("bar: " + x);
    }

    private void car(final Method method,
                     final int    val)
        throws IllegalAccessException,
               IllegalArgumentException,
               InvocationTargetException
    {
        method.invoke(this, val);
    }
}

其他回答

对于每个“函数指针”,我会创建一个小的函子类来实现你的计算。 定义一个所有类都将实现的接口,并将这些对象的实例传递到更大的函数中。这是“命令模式”和“战略模式”的结合。

@sblundy的例子很好。

从Java8开始,您可以使用lambdas,它在官方SE 8 API中也有库。

用法: 您只需要使用带有一个抽象方法的接口。 创建一个实例(你可能想使用java SE 8已经提供的实例),如下所示:

Function<InputType, OutputType> functionname = (inputvariablename) {
... 
return outputinstance;
}

有关更多信息,请查看文档:https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html

谷歌Guava库正在变得非常流行,它有一个通用的Function和Predicate对象,它们已经在API的许多部分中使用了这些对象。

您需要创建一个接口,该接口提供您希望传递的函数。例如:

/**
 * A simple interface to wrap up a function of one argument.
 * 
 * @author rcreswick
 *
 */
public interface Function1<S, T> {

   /**
    * Evaluates this function on it's arguments.
    * 
    * @param a The first argument.
    * @return The result.
    */
   public S eval(T a);

}

然后,当你需要传递一个函数时,你可以实现这个接口:

List<Integer> result = CollectionUtilities.map(list,
        new Function1<Integer, Integer>() {
           @Override
           public Integer eval(Integer a) {
              return a * a;
           }
        });

最后,map函数使用Function1中传递的参数,如下所示:

   public static <K,R,S,T> Map<K, R> zipWith(Function2<R,S,T> fn, 
         Map<K, S> m1, Map<K, T> m2, Map<K, R> results){
      Set<K> keySet = new HashSet<K>();
      keySet.addAll(m1.keySet());
      keySet.addAll(m2.keySet());

      results.clear();

      for (K key : keySet) {
         results.put(key, fn.eval(m1.get(key), m2.get(key)));
      }
      return results;
   }

如果您不需要传递参数,您通常可以使用Runnable而不是自己的接口,或者您可以使用各种其他技术使参数计数不那么“固定”,但这通常是与类型安全的权衡。(或者你可以重写你的函数对象的构造函数,以这种方式传递参数。有很多方法,其中一些在特定情况下效果更好。)

看看lambdaj

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

特别是它新的闭包特征

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

你会发现一种非常易读的方式来定义闭包或函数指针,而无需创建无意义的接口或使用丑陋的内部类