什么是最近的替代品功能的指针在Java?
-
02-07-2019 - |
题
我有一个方法就是大约十个行代码。我想要创造更多的方法做同样的事情,除了一个小型计算的,将会改变一行代码。这是一个完美的应用程序,用于通过在一个功能的指针,以替换那一条线,但是Java没有功能的指针。什么是我最好的选择吗?
解决方案
匿名内部课程
假设您希望使用返回 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中,您可以使用lambda表达式调用它:
ref.takingMethod(param -> bodyExpression);
其他提示
如果您可以在该行中执行预定义数量的不同计算,则使用枚举是一种快速但清晰的方法来实现策略模式。
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);
}
显然,策略方法声明以及每个实现的一个实例都是在单个类/文件中定义的。
您需要创建一个提供要传递的功能的界面。例如:
/**
* 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而不是您自己的界面,或者您可以使用各种其他技术来使参数数量减少“固定”。但它通常是类型安全的权衡。 (或者你可以覆盖你的函数对象的构造函数,以这种方式传递params ..有很多方法,有些方法在某些情况下更好。)
使用 ::
运算符
的方法引用
您可以在方法接受功能接口的方法参数中使用方法引用。功能接口是仅包含一个抽象方法的任何接口。 (功能接口可能包含一个或多个默认方法或静态方法。)
IntBinaryOperator
是一个功能界面。它的抽象方法, applyAsInt
,接受两个 int
作为参数,并返回 int
。 Math.max
还接受两个 int
并返回 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);
}
});
有关详细信息,请参阅Java 8中的 ::(双冒号)运算符和 Java语言规范&#167; 15.13 。
你也可以这样做(在某些 RARE 场合有意义)。问题(这是一个大问题)是你失去了使用类/接口的所有类型安全性,你必须处理不存在该方法的情况。
确实有“好处”。您可以忽略访问限制并调用私有方法(示例中未显示,但您可以调用编译器通常不会让您调用的方法)。
同样,这是一种罕见的情况,这是有道理的,但在这种情况下,这是一个很好的工具。
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)语句。
您可能也有兴趣了解Java 7涉及闭包的工作:
http://gafter.blogspot.com/2006/08 /closures-for-java.html 结果 http://tech.puredanger.com/java7/#closures
使用 ::
运算符的新Java 8 功能接口和方法参考。
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();
}
}
那么,我们在这里有什么?
- 功能接口 - 这是接口,注释或不带 @FunctionalInterface ,它只包含一个方法声明。
- 方法参考 - 这只是特殊的语法,如下所示, objectInstance :: methodName ,仅此而已。
- 用法示例 - 只是一个赋值运算符,然后是接口方法调用。 醇>
你应该只为那些听众使用功能接口,而且只能这样做!
因为所有其他类似的函数指针对于代码可读性和理解能力都非常糟糕。但是,直接的方法引用有时会很方便,例如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 Libraries,它具有与Adrian Petrescu上面提到的类似的功能和语法。
有关其他研究,请参阅 Java 8 Cheatsheet
感谢The Guy with the Hat的 Java语言规范&#167; 15.13 链接。
@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);
但是一成不变的类更安全-我不能拿出一个理由,让一个类似这种可变的。
我真的只是因为我感到害怕,每当我听到匿名内部流的-我已经看到了很多冗余的代码是"必需",因为第一件事程序员做的是去匿名的时候,他应该用一个实际的类并不重新考虑他的决定。
听起来像是一种策略模式。查看fluffycat.com Java模式。
在Java编程时我真正想念的一件事是函数回调。一种情况是,这些需要保持自我呈现的是递归处理层次结构,您希望为每个项目执行某些特定操作。就像走一个目录树,或者处理数据结构一样。我内心的极简主义者不得不为每个特定情况定义一个接口,然后是一个实现。
有一天,我发现自己想知道为什么不呢?我们有方法指针 - Method对象。通过优化JIT编译器,反射调用实际上不再带来巨大的性能损失。除了旁边,比如说,将文件从一个位置复制到另一个位置,反映的方法调用的成本就变得无足轻重。
当我更多地考虑它时,我意识到OOP范例中的回调需要将对象和方法绑定在一起 - 输入Callback对象。
查看基于反射的 Java回调解决方案。免费使用。
oK,这个帖子已经足够老了,所以很可能我的答案对这个问题没有帮助。但是由于这个线程帮助我找到了我的解决方案,无论如何我都会把它放在这里。
我需要使用具有已知输入和已知输出的可变静态方法( 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);
其中activity是任意的double值。正如SoftwareMonkey所做的那样,我正在考虑做一些更抽象和概括的事情,但目前我很满意它的方式。三行代码,没有必要的类和接口,这不是太糟糕。
为没有函数数组的接口做同样的事情:
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) { a.g(x); } },
new NameFuncPair("B") { @Override void f(String x) { b.h(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); } }
查看lambdaj
http://code.google.com/p/lambdaj/
,特别是它的新闭包功能
http://code.google.com/p/lambdaj/wiki/Closures
你会发现一种非常易读的方法来定义闭包或函数指针而不创建无意义的接口或使用丑陋的内部类
哇,为什么不创建一个Delegate类,并不是我已经为java做过的那么难,并使用它来传递参数,其中T是返回类型。我很抱歉,作为一名C ++ / C#程序员,一般只是学习java,我需要函数指针,因为它们非常方便。如果您熟悉任何处理方法信息的课程,您可以这样做。在java库中,它将是java.lang.reflect.method。
如果您始终使用界面,则始终必须实施该界面。在事件处理中,从处理程序列表中注册/取消注册确实没有更好的方法,但是对于需要传递函数而不是值类型的委托,使委托类处理它以用于外部接口。
Java 8的答案都没有给出一个完整的,有凝聚力的例子,所以它来了。
声明接受“函数指针”的方法如下:
void doCalculation(Function<Integer, String> calculation, int parameter) {
final String result = calculation.apply(parameter);
}
通过为函数提供lambda表达式来调用它:
doCalculation((i) -> i.toString(), 2);
如果有人正在努力传递一个函数,该函数需要一组参数来定义它的行为,而另一组参数要执行,比如Scheme的:
(define (function scalar1 scalar2)
(lambda (x) (* x scalar1 scalar2)))
从Java8开始,您可以使用lambdas,它也在官方SE 8 API中有库。
<强>用法:强> 您需要使用只有一个抽象方法的接口。 制作一个实例(你可能想要使用已提供的java SE 8),如下所示:
Function<InputType, OutputType> functionname = (inputvariablename) {
...
return outputinstance;
}
有关详细信息,请查看文档: https://docs.oracle的.com / JavaSE的/教程/ JAVA / javaOO / lambdaexpressions.html
在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);