Java中通过名称动态调用函数的最惯用方法是什么?

时间:2015-08-06 02:40:59

标签: java dry

我有一组特定的操作,我希望能够按名称动态访问。

如果我使用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>的操作名称和一个使用带有每个操作大小写的开关的函数,但是这需要我重复每个操作名称两次,这使得代码更加脆弱,编写和维护更加繁琐

对于Java中的这类事情,是否有一个相当简洁的DRY模式?

3 个答案:

答案 0 :(得分:3)

这在Java 8中使用lambdas非常简洁:

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");

答案 1 :(得分:2)

true

答案 2 :(得分:1)

您可以在不使用Java8的情况下在Java中执行相同的操作:

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.");
    }
}

如果您愿意,也可以将该匿名类移动到单独的文件中。

如果您需要不同类型的参数,则必须使用泛型或将参数类型更改为Object,然后进行强制转换。不是很漂亮,但这是静态打字的代价。

如果您想在同一个地图中存储不同类型的操作,编译器也会向您发出警告(使用原始Operation),但这里没什么可做的。一种方法是为不同类型制作几张地图。