我有算法给我一个整数。 基于这个Integer,我想调用一个方法。 每个Integer都是唯一的(如数据库中的主键),并且有一个方法可以调用。每个方法都返回相同的数据类型。这些方法都在同一个类中,并在此类中调用。
经过几个小时的搜索,我只得到了这2个解决方案,但我不知道哪个是更好的"? (运行时间,资源)
切换解决方案: 第一个想法,但感觉不是很好
switch (code) {
case 1:
nextOperation = doMethod1();
break;
case 2:
nextOperation = doMethod2();
break;
//many more cases...
default:
break;
}
public MyObject doMethod1(MyObject myObject){
//do something with operation
return myObject;
}
反射解决方案:可能运行时间不好(?)
try{
String methodName = "doMethod" + Integer.toString(operation.getOperationCode());
//operation.getOperationCode() same like code in switch solution
Method method = this.class.getDeclaredMethod(methodName, parametertype);
nextOperation = (MyObject) method.invoke(this, parameter);
}
catch (Exception e){
LogReport.writeLog(e.toString()); //own Log-Report filewriter
}
对于我的问题或其他解决方案,是否有更好的方法? 如果你能给我一点提示,我会很高兴。
答案 0 :(得分:5)
第三种选择是建立一个从数字到Runnables
的地图,并查找要调用的方法。我不确定运行时间会如何比较,但我想它会比使用反射更快。
Map<Integer, Runnable> methodMap = new ConcurrentHashMap<>();
methodMap.put(1, () -> doMethod1());
methodMap.put(2, () -> doMethod2());
methodMap.put(3, () -> doMethod3());
// ... and so on ...
// look up the method and run it:
Runnable method = methodMap.get(code);
if (method != null) {
method.run();
}
我正在使用ConcurrentHashMap
以防万一这个地图需要在程序运行时动态修改,但是如果你在开始时只构建一次地图然后再修改它,普通的HashMap
也可以这样做。我使用lambdas创建Runnables
来调用每个方法。
构建地图的代码仍然很长。您可以通过使用反射来帮助构建地图来缩短它。这样会慢一些,但是你只需要在构建地图时支付一次反射罚款,而不是每次需要按编号调度方法。
注意:这是在不使用lambdas的情况下将方法添加到地图的另一种方法:
methodMap.put(1, new Runnable() { public void run() { doMethod1(); } });
methodMap.put(2, new Runnable() { public void run() { doMethod2(); } });
methodMap.put(3, new Runnable() { public void run() { doMethod3(); } });
// etc.
这就是匿名内部类的完成方式; lambdas本质上是匿名方法,不带参数()
,->
之后的表达式是要调用的代码(例如doMethod1()
);编译器发现这是传递给put
的{{1}}方法,并将匿名方法作为Map<Integer, Runnable>
的{{1}}方法,并使用该代码创建run
。
答案 1 :(得分:3)
您可以使用:
Map<Integer, Supplier<Operation>> map = new ...;
map.put(1, () -> doMethod1());
map.put(2, () -> doMethod2());
然后致电:
nextOperation = map.get(operationCode).get();
答案 2 :(得分:1)
如果您使用的是Java 8,则可以尝试使用lambda调用方法,将它们映射到整数HashMap。
<强>接口强>
public interface MyInterface {
void excecute();
}
初始化方法:
private int i = 0;
...
...
HashMap<Integer, MyInterface> myMap = new HashMap<>();
myMap.put(0, () -> {
i = doMethod0();
});
调用方法:
myMap.get(i).excecute();
答案 3 :(得分:1)
另一种方法(与Java 7兼容,虽然我不知道Android是否支持)是使用MethodHandles
。它们提供了反射的优势,因为您不必编写所有代码来填充映射(尽管可以编写脚本来生成代码,因为这可能只需要执行一次),但它们由于访问检查是在执行查找时提前执行的,因此比反射更快。
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.HashMap;
import java.util.Map;
Map<Integer, MethodHandle> handleByNumber = new HashMap<>();
MethodHandles.Lookup = MethodHandles.lookup();
MethodType mt = MethodType.methodType(MyObject.class, MyObject.class);
int number = 1; // find all doMethodN methods from 1 up to whatever
while (true) {
try {
MethodHandle mh = lookup.findStatic(MyClass.class, "doMethod" + number, mt);
handles.put(number, mh);
number++;
} catch (NoSuchMethodException | IllegalAccessException e) {
break;
}
}
请注意,这假定方法是static
,方法所在的类名为MyClass
,并且方法采用并返回问题中的MyObject
,但是这个问题并不一致。这些都必须改变。
如果方法不是静态的,则会使用lookup.findVirtual
代替lookup.findStatic
。
按编号调用方法,其中obj
是参数:
MethodHandle mh = handles.get(code);
if (mh != null) {
try {
MyObject result = (MyObject) mh.invokeExact(obj);
} catch (Throwable e) {
throw new RuntimeException(e); // or other handling as appropriate
}
}
如果方法不是静态的,则需要在调用中提供接收者(您调用方法的对象):
MyObject result = (MyObject) mh.invokeExact(receiver, obj);