我有一个方法,大约有十行代码。我想创建更多的方法来做完全相同的事情,除了一个会改变一行代码的小计算。这是一个传递函数指针来替换这一行的完美应用程序,但是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);
但是不可变的类更安全——我找不到一个理由让这样的类成为可变的。
我之所以发布这篇文章,是因为每当我听到匿名的内部类时,我就会感到畏缩——我看到过很多“必需”的冗余代码,因为程序员做的第一件事就是在他应该使用实际类的时候使用匿名,并且从来没有重新考虑过他的决定。
其他回答
您需要创建一个接口,该接口提供您希望传递的函数。例如:
/**
* 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而不是自己的接口,或者您可以使用各种其他技术使参数计数不那么“固定”,但这通常是与类型安全的权衡。(或者你可以重写你的函数对象的构造函数,以这种方式传递参数。有很多方法,其中一些在特定情况下效果更好。)
@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编程时,我真正怀念的一件事是函数回调。在递归处理层次结构中,需要不断地呈现这些元素,您希望为每个元素执行特定的操作。比如遍历目录树,或者处理数据结构。我内心的极简主义者讨厌为每个特定的情况定义接口和实现。
有一天我发现自己在想为什么不呢?我们有方法指针——method对象。通过优化JIT编译器,反射调用实际上不再带来巨大的性能损失。此外,除了将文件从一个位置复制到另一个位置之外,反射方法调用的成本变得微不足道。
随着我对它的深入思考,我意识到OOP范式中的回调需要将对象和方法绑定在一起——进入回调对象。
请查看我的基于反射的Java回调解决方案。免费供任何用途。
你可能也会对Java 7中涉及闭包的工作感兴趣:
Java闭包的当前状态是什么?
http://gafter.blogspot.com/2006/08/closures-for-java.html http://tech.puredanger.com/java7/#closures
如果有人试图传递一个函数,该函数使用一组参数来定义其行为,但使用另一组参数来执行,就像Scheme的:
(define (function scalar1 scalar2)
(lambda (x) (* x scalar1 scalar2)))
参见Java中参数定义行为的传递函数
推荐文章
- 如何分割逗号分隔的字符串?
- Java字符串—查看字符串是否只包含数字而不包含字母
- Mockito.any()传递带有泛型的接口
- 在IntelliJ 10.5中运行测试时,出现“NoSuchMethodError: org.hamcrest. matcher . descripbemismatch”
- 使用String.split()和多个分隔符
- Java数组有最大大小吗?
- 在Android中将字符串转换为Uri
- 从JSON生成Java类?
- 为什么java.util.Set没有get(int index)?
- Swing和AWT的区别是什么?
- 为什么Java流是一次性的?
- 四舍五入BigDecimal *总是*有两位小数点后
- 设计模式:工厂vs工厂方法vs抽象工厂
- 为什么函数指针定义可以使用任意数量的&号或星号* ?
- Java:检查enum是否包含给定的字符串?