在Java中,函数指针最接近什么替代?
我有一个约十行代码的方法。 我想创build更多的方法来完成同样的事情,除了一个小的计算,将改变一行代码。 这是传递函数指针来replace那一行的完美应用程序,但Java没有函数指针。 我最好的select是什么?
匿名的内部类
假设你想要传递一个返回int
的String
参数的函数。
首先,如果不能重复使用现有的函数,则必须定义一个接口函数作为其唯一成员。
interface StringFunction { int func(String param); }
一个接受指针的方法就会像下面这样接受StringFunction
实例:
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中,你可以用一个lambdaexpression式来调用它:
ref.takingMethod(param -> bodyExpression);
对于每个“函数指针”,我会创build一个实现您的计算的小函数类 。 定义所有类将实现的接口,并将这些对象的实例传递给更大的函数。 这是“ 指挥模式 ”和“ 战略模式 ”的结合。
@ sblundy的例子是好的。
当有一个预定义数量的不同的计算,你可以在一行中使用枚举是一个快速,但明确的方式来实现一个战略模式。
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); }
显然,策略方法声明以及每个实现的一个实例都是在一个类/文件中定义的。
您需要创build一个接口,提供您想要传递的function。 例如:
/** * 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函数使用函数1中传递如下:
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而不是你自己的接口,或者你可以使用各种其他的技术来使得参数数量不那么“固定”,但这通常是一种types安全的折衷。 (或者你可以重写你的函数对象的构造函数来传递这些参数..有很多方法,有些在某些情况下工作得更好。)
你也可以做到这一点(在一些RARE场合是有道理的)。 这个问题(也是一个大问题)是你失去了使用类/接口的所有types安全,你必须处理方法不存在的情况。
它具有可以忽略访问限制和调用私有方法的“好处”(在示例中未显示,但可以调用编译器通常不允许调用的方法)。
再说一次,这是很有意义的,但在这种情况下,这是一个很好的工具。
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); } }
如果你只有一行是不同的,你可以添加一个参数,例如一个标志和一个调用一行或另一行的if(flag)语句。
使用::
运算符的方法引用
您可以在方法接受function接口的方法参数中使用方法引用。 function接口是只包含一个抽象方法的任何接口。 (一个function接口可能包含一个或多个默认方法或静态方法。)
IntBinaryOperator
是一个function界面。 其抽象方法applyAsInt
接受两个int
s作为其参数并返回一个int
。 Math.max
也接受两个int
并返回一个int
。 在这个例子中, A.method(Math::max);
使parameter.applyAsInt
将其两个input值发送到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); } });
有关更多信息,请参阅Java 8和Java语言规范第15.13节中的 ::(双冒号)运算符 。
您可能也有兴趣了解Java 7涉及闭包的工作:
Java中closures的状态是什么?
http://gafter.blogspot.com/2006/08/closures-for-java.html
http://tech.puredanger.com/java7/#closures
@ sblundy的答案很好,但匿名内部类有两个小缺陷,主要是他们倾向于不可重用,次要是一个庞大的语法。
好的是,他的模式扩展到完整的类没有任何改变的主要类(执行计算)。
当你实例化一个新的类时,你可以将parameter passing给可以在你的方程中作为常量的类 – 所以如果你的一个内部类是这样的:
f(x,y)=x*y
但有时你需要一个:
f(x,y)=x*y*2
也许还有三分之一是:
f(x,y)=x*y/2
而不是创build两个匿名的内部类或添加“passthrough”参数,您可以创build一个实例化的ACTUAL类:
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 function接口和方法引用使用::
运算符。
Java 8能够使用“ @ Functional Interface ”指针维护方法引用(MyClass :: new)。 不需要相同的方法名称,只需要相同的方法签名。
例:
@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(); } }
那么,我们在这里?
- function接口 – 这是接口,注释或不包含@FunctionalInterface ,它只包含一个方法声明。
- 方法引用 – 这只是特殊的语法,看起来像这样, objectInstance :: methodName ,没有什么比这更less了。
- 用法示例 – 只是一个赋值运算符,然后是接口方法调用。
您应该只为听众使用function接口!
因为所有其他这样的函数指针对代码可读性和理解能力都是非常糟糕的。 但是,直接的方法引用有时候会很方便,例如foreach。
有几个预定义的function接口:
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库,它具有与Adrian Petrescu上面提到的相似的function和语法。
有关更多的研究请看Java 8 Cheatsheet
并感谢The Hat with the Java Language Specification§15.13链接。
正在变得非常stream行的谷歌Guava库有一个通用的Function和Predicate对象,他们已经在他们的API的许多部分工作。
没有一个函数数组的接口来做同样的事情:
class NameFuncPair { public String name; // name each func void f(String x) {} // stub gets overridden public NameFuncPair(String myName) { this.name = myName; } } public class ArrayOfFunctions { public static void main(String[] args) { final A a = new A(); final B b = new B(); NameFuncPair[] fArray = new NameFuncPair[] { new NameFuncPair("A") { @Override void f(String x) { ag(x); } }, new NameFuncPair("B") { @Override void f(String x) { bh(x); } }, }; // Go through the whole func list and run the func named "B" for (NameFuncPair fInstance : fArray) { if (fInstance.name.equals("B")) { fInstance.f(fInstance.name + "(some args)"); } } } } class A { void g(String args) { System.out.println(args); } } class B { void h(String args) { System.out.println(args); } }
听起来像一个战略模式给我。 看看fluffycat.com的Java模式。
在Java中编程时,我真的很想念的一件事是函数callback。 一种情况是,需要这些继续呈现自己在recursion处理层次结构,你想要为每个项目执行一些具体的行动。 就像走一个目录树,或者处理一个数据结构一样。 我内心的极简主义者不得不定义一个接口,然后为每个特定的情况下实现。
有一天,我发现自己想知道为什么不呢? 我们有方法指针 – Method对象。 通过优化JIT编译器,reflection调用实际上不会带来巨大的性能损失。 除了在一个位置复制文件之外,所反映的方法调用的代价也是微不足道的。
当我想到更多关于它的知识时,我意识到在OOP范例中的callback需要绑定一个对象和一个方法 – inputcallback对象。
查看我的基于reflection的Java解决scheme。 免费使用。
oK,这个线程已经够老了,所以很可能我的答案对这个问题没有帮助。 但是,由于这个线程帮助我find了解决scheme,无论如何我都会把它放在这里。
我需要使用已知input和已知输出的variables静态方法(都是double )。 那么,知道方法包和名称,我可以工作如下:
java.lang.reflect.Method Function = Class.forName(String classPath).getMethod(String method, Class[] params);
对于接受一个double作为参数的函数。
所以,在我具体的情况下,我把它初始化了
java.lang.reflect.Method Function = Class.forName("be.qan.NN.ActivationFunctions").getMethod("sigmoid", double.class);
并在稍后与更复杂的情况下调用它
return (java.lang.Double)this.Function.invoke(null, args); java.lang.Object[] args = new java.lang.Object[] {activity}; someOtherFunction() + 234 + (java.lang.Double)Function.invoke(null, args);
活动是一个任意的双重价值。 我正在考虑做一些比较抽象的概括性的事情,就像SoftwareMonkey所做的那样,但是现在我已经很满意了。 三行代码,没有必要的类和接口,这也不算太坏。
检查出lambdaj
http://code.google.com/p/lambdaj/
特别是其新的closuresfunction
http://code.google.com/p/lambdaj/wiki/Closures
你会发现一个非常可读的方式来定义闭包或函数指针,而不会创build无意义的接口或使用丑陋的内部类
哇,为什么不创build一个Delegate类,这并不是那么辛苦,因为我已经为java做了,并使用它来传入参数,其中T是返回types。 我很抱歉,但作为一般的C + + / C#程序员只是学习Java,我需要函数指针,因为它们非常方便。 如果你熟悉任何涉及方法信息的类,你可以做。 在java库中,这将是java.lang.reflect.method。
如果你总是使用一个接口,你总是必须实现它。 在事件处理中,从处理程序列表中注册/注销实际上没有更好的方法,但是对于需要传递函数而不是值types的委托,使委托类能够处理它的外部接口。
如果任何人正在努力传递一个函数,该函数使用一组参数来定义其行为,而另一组参数将在其上执行,如Scheme的:
(define (function scalar1 scalar2) (lambda (x) (* x scalar1 scalar2)))
请参阅在Java中使用参数定义的行为来传递函数
自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
在Java 8之前,类似于函数指针的function的最接近的替代品是匿名类。 例如:
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中,我们有一个非常整洁的替代方法,称为lambdaexpression式 ,可以用作:
list.sort((a, b) -> { a.isBiggerThan(b) } );
其中isBiggerThan是CustomClass中的一个方法。 我们也可以在这里使用方法引用:
list.sort(MyClass::isBiggerThan);
没有一个Java 8的答案给出了一个完整的,有凝聚力的例子,所以在这里。
声明接受“函数指针”的方法如下:
void doCalculation(Function<Integer, String> calculation, int parameter) { final String result = calculation.apply(parameter); }
通过提供带有lambdaexpression式的函数来调用它:
doCalculation((i) -> i.toString(), 2);