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


当前回答

@sblundy的回答很好,但匿名内部类有两个小缺陷,主要是它们往往不可重用,其次是庞大的语法。

好处是他的模式扩展到完整的类,而不需要对主类(执行计算的类)进行任何更改。

当你实例化一个新类时,你可以将参数传递到这个类中,这些参数可以作为等式中的常量——所以如果你的一个内部类看起来像这样:

f(x,y)=x*y

但有时你需要的是:

f(x,y)=x*y*2

也许第三个原因是:

f(x,y)=x*y/2

而不是创建两个匿名的内部类或添加一个“传递”参数,你可以创建一个实例化为:

InnerFunc f=new InnerFunc(1.0);// for the first
calculateUsing(f);
f=new InnerFunc(2.0);// for the second
calculateUsing(f);
f=new InnerFunc(0.5);// for the third
calculateUsing(f);

它将简单地将常量存储在类中,并在接口中指定的方法中使用它。

事实上,如果知道你的函数不会被存储/重用,你可以这样做:

InnerFunc f=new InnerFunc(1.0);// for the first
calculateUsing(f);
f.setConstant(2.0);
calculateUsing(f);
f.setConstant(0.5);
calculateUsing(f);

但是不可变的类更安全——我找不到一个理由让这样的类成为可变的。

我之所以发布这篇文章,是因为每当我听到匿名的内部类时,我就会感到畏缩——我看到过很多“必需”的冗余代码,因为程序员做的第一件事就是在他应该使用实际类的时候使用匿名,并且从来没有重新考虑过他的决定。

其他回答

在Java 8之前,类似函数指针的功能最接近的替代品是匿名类。例如:

Collections.sort(list, new Comparator<CustomClass>(){
    public int compare(CustomClass a, CustomClass b)
    {
        // Logic to compare objects of class CustomClass which returns int as per contract.
    }
});

但现在在Java 8中,我们有一个非常简洁的替代方法,称为lambda表达式,它可以用作:

list.sort((a, b) ->  { a.isBiggerThan(b) } );

其中isBiggerThan是CustomClass中的一个方法。我们也可以在这里使用方法引用:

list.sort(MyClass::isBiggerThan);

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

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

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

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);
    }
}

如果有人试图传递一个函数,该函数使用一组参数来定义其行为,但使用另一组参数来执行,就像Scheme的:

(define (function scalar1 scalar2)
  (lambda (x) (* x scalar1 scalar2)))

参见Java中参数定义行为的传递函数

如果你只有一个不同的行,你可以添加一个参数,比如一个标志和一个If (flag)语句,它调用一行或另一行。

@sblundy的回答很好,但匿名内部类有两个小缺陷,主要是它们往往不可重用,其次是庞大的语法。

好处是他的模式扩展到完整的类,而不需要对主类(执行计算的类)进行任何更改。

当你实例化一个新类时,你可以将参数传递到这个类中,这些参数可以作为等式中的常量——所以如果你的一个内部类看起来像这样:

f(x,y)=x*y

但有时你需要的是:

f(x,y)=x*y*2

也许第三个原因是:

f(x,y)=x*y/2

而不是创建两个匿名的内部类或添加一个“传递”参数,你可以创建一个实例化为:

InnerFunc f=new InnerFunc(1.0);// for the first
calculateUsing(f);
f=new InnerFunc(2.0);// for the second
calculateUsing(f);
f=new InnerFunc(0.5);// for the third
calculateUsing(f);

它将简单地将常量存储在类中,并在接口中指定的方法中使用它。

事实上,如果知道你的函数不会被存储/重用,你可以这样做:

InnerFunc f=new InnerFunc(1.0);// for the first
calculateUsing(f);
f.setConstant(2.0);
calculateUsing(f);
f.setConstant(0.5);
calculateUsing(f);

但是不可变的类更安全——我找不到一个理由让这样的类成为可变的。

我之所以发布这篇文章,是因为每当我听到匿名的内部类时,我就会感到畏缩——我看到过很多“必需”的冗余代码,因为程序员做的第一件事就是在他应该使用实际类的时候使用匿名,并且从来没有重新考虑过他的决定。