【问题标题】:Using reflection instead of a long switch statement使用反射而不是长的 switch 语句
【发布时间】:2020-06-04 06:27:28
【问题描述】:

嘿,所以我有一个很长的 switch 语句,它调用一个基于这样的字符串的方法

private void callMethod(String name) {
    switch(name) {
        case "blah":
            blah();
            break;
        case "method":
            method()
            break;
    }
}

等等

所以我用反射来缩短代码

try {
    Method method = ClassName.class.getDeclaredMethod(name, args);
    method.invoke(args);
} catch (NoSuchMethodException e) {
    // switch default
}

只是想知道使用这样的反射是否对内存/性能不利。我只是偶尔调用这个函数。

编辑:我使用它的原因是因为用户可以选择根据他们输入的字符串来启动游戏的不同部分。启动事件的每个方法都以他们需要输入的内容命名。

【问题讨论】:

  • 如果用户有能力控制你的 call 方法函数的输入是有风险的,因为它可能会暴露他们不打算运行的代码。
  • 反射确实会对应用程序的性能产生影响,可以使用分析器来确定精确的差异。但是,这似乎是一个 xy 问题 (en.wikipedia.org/wiki/XY_problem)。你能描述一下导致你创建这个函数的需求吗?可能有一种解决方案既不需要硬编码的 switch 语句也不需要反射代码。
  • 很难在没有看到您的用例的情况下回答。如果这只是在内部调用并且性能无关紧要,反射可能是一个很好的解决方案。
  • 该类中唯一的方法是我想要调用的方法。没有他们可以调用的非预期方法。我调用这个函数的方式是当用户输入他们自己的字符串时。每种方法开始游戏的不同部分。
  • 几乎没有关于是什么促使您编写这段代码的输入,但是为什么您不执行以下操作而不是 new MyClass(). callMethod("foo")new MyClass(). foo()

标签: java performance memory reflection switch-statement


【解决方案1】:

反思通常是最糟糕的做事方式。它很慢,无法在运行时进行优化,编译器也无法标记错误。而且正如 kpie 所指出的,让用户输入直接控制执行哪个方法是一个安全漏洞。

任何长的switch 语句都可以替换为Map。在您的情况下,Map<String, Runnable> 就足够了:

Map<String, Runnable> actions = new HashMap<>();
actions.put("blah", this::blah);
actions.put("method", this::method);

// ...

Runnable action = actions.get(name);
if (action == null) {
    throw new IllegalArgumentException("Invalid name: " + name);
}
action.run();

【讨论】:

  • 说“它不能在运行时优化”是不正确的。优化更加困难,并且可能会遇到更多实现特定的限制。由于性能是次要问题,因此您应该从编译时安全开始。然后,维护(例如重构支持)。然后,潜在的性能问题......现在,考虑一下,还有另一个问题,安全问题。如果有人传递了一个不打算支持但恰好匹配现有方法的字符串,会发生什么......
  • 支持该类中的每个方法
  • @NicholasM 如果一年后您或其他人向该类添加公共方法,您/他们会记得任何最终用户都有权调用它吗?
  • 是的,这对我来说只是一个小项目。我可以看到这将是一个问题,但它并没有真正影响到我
  • 它甚至可以无意中调用private 方法。 @NicholasM 这可能是编程中最基本的误解,“我将永远记住这个约束/永远不必更改或重用此代码
【解决方案2】:

像这样使用反射是个坏主意。查看here 了解有关缺点的信息。

关于性能:

“由于反射涉及动态解析的类型,某些Java虚拟机优化无法执行。因此,反射操作的性能比非反射操作慢,应避免在经常调用的代码段中性能敏感的应用程序。”

不要将反射视为StringArrayList 之类的普通工具,而应尝试实现适合您需求的更好的软件设计。那么你将不得不很少考虑反射。


编辑:

性能损失当然值得商榷,但请注意其他缺点。正如其他人已经说过的那样,存在安全问题,例如当用户“调用”一个不打算由用户直接“调用”的方法时。另一个缺点是您的代码中可能存在由于反射而在编译时无法找到的问题。还有更多的缺点...

在我看来,反射应该主要用于解决与生产代码没有直接关系的技术问题,例如在测试您的软件时。

【讨论】:

  • 我需要这个的原因是因为输入字符串的是用户。那我应该只使用一个很长的 switch 语句吗?
  • 我不知道你程序的其余部分,所以我会说是的。由于反射的缺点,只需使用长 switch 语句。但我想我最初的答案是严格的,而且关于反射也更笼统。因此,如果您认为在这里缩短内容是值得的,那么请继续使用它。 ^^
  • @NicholasM 用户需要几秒钟来输入字符串。反射可能会花费几纳秒。还有更多的性能想法吗?不过,反射感觉很老套。
猜你喜欢
  • 2011-11-02
  • 2020-10-02
  • 1970-01-01
  • 2013-11-17
  • 1970-01-01
  • 2012-11-03
  • 1970-01-01
  • 1970-01-01
  • 2010-10-01
相关资源
最近更新 更多