Java 中按名称动态调用函数的最惯用方法是什么?
What's the most idiomatic way in Java to dynamically call a function by name?
我有一组特定的操作,我希望能够通过名称动态访问这些操作。
如果我使用 JavaScript,我会在字典中表示它们,操作名称作为键,操作函数作为值。
然后,例如,我可以向用户询问操作的名称,如果存在则显示操作结果,如果不存在则显示错误消息,如下所示:
var operations = {
addition: function(a, b) { return a + b; },
subtraction: function(a, b) { return a - b; },
multiplication: function(a, b) { return a * b; }
// etc.
};
var a = 4, b = 7;
var opName = prompt("Enter operation name:");
if (opName in operations) {
alert("Result: " + operations[opName](a, b));
} else {
alert("The operation '" + opName + "' does not exist.");
}
我如何在 Java 中做同样的事情?我可以有一个 Set<String>
的操作名称和一个函数,该函数使用带有 case 的 switch 来处理每个操作,但这需要我将每个操作名称重复两次,这使得代码更脆弱,编写和维护起来也更乏味.
在 Java 中是否有针对此类事情的相当简洁的 DRY 模式?
public interface Function {
double run(double a, double b);
}
public class addFunction implements Function {
double run(double a, double b) {
return a+b;
}
}
//...
Map<String, Function> operations = new HashMap<string, Function>();
operations.put("add", new addFunction());
//...
String op;
double a, b;
operations.get(op).run(a, b);
您可以在 Java 中执行相同的操作,而无需使用 Java8:
public interface Operation<T,R> {
R perform(T... args);
}
public void test() {
Map<String, Operation> operations = new HashMap<String, Operation>() {
{
this.put("addition", new Operation<Integer, Integer>() {
public Integer perform(Integer... args) {
return args[0] + args[1];
}});
}
};
String operation = "";
Integer a = 1;
Integer b = 1;
if (operations.containsKey(operation)) {
System.out.println("Result: " + operations.get(operation).perform(a, b));
} else {
System.out.println("The operation '" + operation + "' does not exist.");
}
}
如果您愿意,也可以将匿名 class 移动到单独的文件中。
如果您需要不同类型的参数,您将不得不使用泛型或将参数类型更改为 Object
然后进行强制转换。不漂亮,但这是静态类型的代价。
此外,编译器会向您发出警告(使用原始 Operation
),但如果您想在同一个映射中存储不同类型的操作,则无需在此做太多事情。一个解决办法是为不同类型制作几张地图。
在 Java 8 中使用 lambda 更简洁:
Map<String, BinaryOperator<Integer>> operators = new TreeMap<>();
operators.put("add", (n1, n2) -> n1 + n2);
operators.put("minus", (n1, n2) -> n1 - n2);
if (operators.containsKey(opName)) {
return operators.get(opName).apply(n1, n2);
}
但我从您的评论中了解到,这不是一种选择。另一种方法是使用枚举来包含您的操作,以便您可以在一个地方添加新操作:
enum Operation {
PLUS {
public int operate(int arg1, int arg2) {
return arg1 + arg2;
}
},
MINUS {
public int operate(int arg1, int arg2) {
return arg1 - arg2;
}
},
...
abstract public int operate(int arg1, int arg2);
}
for (operation: Operations.values()) {
if (operation.name().equals(opName))
System.out.println("result = " + operation.operate(arg1, arg2));
return;
}
}
System.out.println("The Operation " + opName + " does not exist");
我有一组特定的操作,我希望能够通过名称动态访问这些操作。
如果我使用 JavaScript,我会在字典中表示它们,操作名称作为键,操作函数作为值。
然后,例如,我可以向用户询问操作的名称,如果存在则显示操作结果,如果不存在则显示错误消息,如下所示:
var operations = {
addition: function(a, b) { return a + b; },
subtraction: function(a, b) { return a - b; },
multiplication: function(a, b) { return a * b; }
// etc.
};
var a = 4, b = 7;
var opName = prompt("Enter operation name:");
if (opName in operations) {
alert("Result: " + operations[opName](a, b));
} else {
alert("The operation '" + opName + "' does not exist.");
}
我如何在 Java 中做同样的事情?我可以有一个 Set<String>
的操作名称和一个函数,该函数使用带有 case 的 switch 来处理每个操作,但这需要我将每个操作名称重复两次,这使得代码更脆弱,编写和维护起来也更乏味.
在 Java 中是否有针对此类事情的相当简洁的 DRY 模式?
public interface Function {
double run(double a, double b);
}
public class addFunction implements Function {
double run(double a, double b) {
return a+b;
}
}
//...
Map<String, Function> operations = new HashMap<string, Function>();
operations.put("add", new addFunction());
//...
String op;
double a, b;
operations.get(op).run(a, b);
您可以在 Java 中执行相同的操作,而无需使用 Java8:
public interface Operation<T,R> {
R perform(T... args);
}
public void test() {
Map<String, Operation> operations = new HashMap<String, Operation>() {
{
this.put("addition", new Operation<Integer, Integer>() {
public Integer perform(Integer... args) {
return args[0] + args[1];
}});
}
};
String operation = "";
Integer a = 1;
Integer b = 1;
if (operations.containsKey(operation)) {
System.out.println("Result: " + operations.get(operation).perform(a, b));
} else {
System.out.println("The operation '" + operation + "' does not exist.");
}
}
如果您愿意,也可以将匿名 class 移动到单独的文件中。
如果您需要不同类型的参数,您将不得不使用泛型或将参数类型更改为 Object
然后进行强制转换。不漂亮,但这是静态类型的代价。
此外,编译器会向您发出警告(使用原始 Operation
),但如果您想在同一个映射中存储不同类型的操作,则无需在此做太多事情。一个解决办法是为不同类型制作几张地图。
在 Java 8 中使用 lambda 更简洁:
Map<String, BinaryOperator<Integer>> operators = new TreeMap<>();
operators.put("add", (n1, n2) -> n1 + n2);
operators.put("minus", (n1, n2) -> n1 - n2);
if (operators.containsKey(opName)) {
return operators.get(opName).apply(n1, n2);
}
但我从您的评论中了解到,这不是一种选择。另一种方法是使用枚举来包含您的操作,以便您可以在一个地方添加新操作:
enum Operation {
PLUS {
public int operate(int arg1, int arg2) {
return arg1 + arg2;
}
},
MINUS {
public int operate(int arg1, int arg2) {
return arg1 - arg2;
}
},
...
abstract public int operate(int arg1, int arg2);
}
for (operation: Operations.values()) {
if (operation.name().equals(opName))
System.out.println("result = " + operation.operate(arg1, arg2));
return;
}
}
System.out.println("The Operation " + opName + " does not exist");