Java用什么替代函数指针?

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (10)
  • 关注 (0)
  • 查看 (561)

我有一个大约10行的代码。我想在其它的地方调用它,Java中没有函数指针,我该怎么办?

提问于
用户回答回答于

匿名内部类

假设您希望将一个字符串传递给函数,函数返回整形

首先,定义一个接口,然后将该函数定义为该接口的唯一成员。

interface StringFunction {
    int func(String param);
}

调用的时候函数参数为该接口类型:

public void takingMethod(StringFunction sf) {
   int i = sf.func("my string");
   // do whatever ...
}

这样调用它:

ref.takingMethod(new StringFunction() {
    public int func(String param) {
        // body
    }
});

注:在Java 8中,可以使用lambda表达式:

ref.takingMethod(param -> bodyExpression);
用户回答回答于

新Java 8功能接口方法可以参考::操作符。

Java 8能够用“@功能接口“指针。不需要相同的方法名称,只需要相同的方法签名。

例子:

@FunctionalInterface
interface CallbackHandler{
    public void onClick();
}

public class MyClass{
    public void doClick1(){System.out.println("doClick1");;}
    public void doClick2(){System.out.println("doClick2");}
    public CallbackHandler mClickListener = this::doClick;

    public static void main(String[] args) {
        MyClass myObjectInstance = new MyClass();
        CallbackHandler pointer = myObjectInstance::doClick1;
        Runnable pointer2 = myObjectInstance::doClick2;
        pointer.onClick();
        pointer2.run();
    }
}

我们这里有什么?

  1. 功能接口,注释或去除@FunctionalInterface,它只包含一个方法声明。
  2. 方法引用:这只是一个特殊的语法,如下所示,对象实例::方法名
  3. 用法示例 :只是一个赋值操作符,然后调用接口方法。

因为函数指针理解起来比较麻烦。而直接引用很方便,例如foreach。

有几个预定义的功能接口:

Runnable              -> void run( );
Supplier<T>           -> T get( );
Consumer<T>           -> void accept(T);
Predicate<T>          -> boolean test(T);
UnaryOperator<T>      -> T apply(T);
BinaryOperator<T,U,R> -> R apply(T, U);
Function<T,R>         -> R apply(T);
BiFunction<T,U,R>     -> R apply(T, U);
//... and some more of it ...
Callable<V>           -> V call() throws Exception;
Readable              -> int read(CharBuffer) throws IOException;
AutoCloseable         -> void close() throws Exception;
Iterable<T>           -> Iterator<T> iterator();
Comparable<T>         -> int compareTo(T);
Comparator<T>         -> int compare(T,T);

在早期的Java版本中有一个Guava库,它具有类似的功能和语法。

要深入了解,请看Java 8 CheatSheet

用户回答回答于

上面的代码扩展模式为完整的类,而不改变主类。

当您实例化一个新类时,您可以将参数传递到该类中,因此该类可以作为方程中的常量,如果你的内部类之一如下所示:

f(x,y)=x*y

有时是这样:

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

也可能是这样:

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

与其创建两个匿名内部类或添加一个“passthrough”参数,还可以创建一个实例化对象:

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

密封类更安全。

用户回答回答于
用户回答回答于

还可以使用::操作符

您可以在方法参数中使用方法的引用,该方法接受功能接口。功能接口只包含一个抽象方法的接口。(但是功能接口可能包含一个或多个默认方法或静态方法。)

IntBinaryOperator是一个功能接口。它的抽象方法,applyAsInt,接受两个ints作为其参数,并返回int.Math.max也接受两个intS参数并返回int,在这个例子中,A.method(Math::max);创建parameter.applyAsInt并将其两个输入值传送到Math.max并返回结果Math.max.

import java.util.function.IntBinaryOperator;

class A {
    static void method(IntBinaryOperator parameter) {
        int i = parameter.applyAsInt(7315, 89163);
        System.out.println(i);
    }
}
import java.lang.Math;

class B {
    public static void main(String[] args) {
        A.method(Math::max);
    }
}

可以:

method1(Class1::method2);

而不是:

method1((arg1, arg2) -> Class1.method2(arg1, arg2));

简称:

method1(new Interface1() {
    int method1(int arg1, int arg2) {
        return Class1.method2(arg1, agr2);
    }
});
用户回答回答于

如果只有一行是不同的,那么可以添加一个参数,比如If语句,该语句调用一行或另一行。

用户回答回答于

你也可以把它定义成全局静态函数,可是这样会破坏类的安全性,它的好处是,你可以忽略访问限制,直接调用该私有方法

这个情况不是很常见,实例:

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);
    }
}
用户回答回答于

你可以创建一个接口,该接口包含该函数。例:

/**
 * 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函数使用传递的函数如下:

   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接口,或者可以使用其它方法来确定你的参数。

用户回答回答于

还可以使用枚举类型。

public enum Operation {
    PLUS {
        public double calc(double a, double b) {
            return a + b;
        }
    },
    TIMES {
        public double calc(double a, double b) {
            return a * b;
        }
    }
     ...

     public abstract double calc(double a, double b);
}

策略模式要求每个实现的方法都要在类中定义。

用户回答回答于

对于“函数指针”,我会创建一个子类实现了该计算。定义的类要实现的接口,并将这些对象的实例传递到更高级的函数中。这是“指挥模式“,和”战略模式

扫码关注云+社区

领取腾讯云代金券