为什么人们说 Java 不能有表达式求值器?

发布于 2024-10-21 22:32:13 字数 1321 浏览 1 评论 0原文

我知道默认情况下Java没有所谓的eval(我称之为“邪恶”)方法。这听起来像是一件坏事——知道你没有很多其他人拥有的东西。但更糟糕的是,你似乎被告知你不能拥有它。

我的问题是:其背后的坚实推理是什么?我的意思是,谷歌搜索只会返回大量旧数据和虚假原因——即使有我正在寻找的答案,我也无法从那些只是抛出通用标签词的人中过滤掉它。

我对那些告诉我如何解决这个问题的答案不感兴趣;我可以自己做到这一点:

使用 Bean 脚本框架 (BSF)

文件 sample.py (在 py 文件夹中)内容:

def factorial(n): 
    return reduce(lambda x, y:x * y, range(1, n + 1))

和 Java 代码:

ScriptEngine engine = new ScriptEngineManager().getEngineByName("jython");
engine.eval(new FileReader("py" + java.io.File.separator + "sample.py"));
System.out.println(engine.eval("factorial(932)"));

使用设计的桥梁,如 JLink

example

这相当于:

String expr = "N[Integrate[E^(2 y^5)/(2 x^3), {x, 4, 7}, {y, 2, 3}]]";
System.out.println(MM.Eval(expr));
//Output: 1.5187560850359461*^206 + 4.2210685420287355*^190*I

其他方法

  • 使用 Dijkstras shunt-yard 算法或类似算法并从头开始编写表达式求值器。
  • 通过委托和 HashMultimaps 使用复杂的正则表达式和字符串操作。
  • 使用 Java 表达式库
  • 使用 Java 表达式语言
  • 使用符合 JRE 的脚本语言,例如 BeanShell。
  • 使用 Java 汇编器和下面的方法或直接字节码操作(如 Javaassist)。
  • 使用 Java 编译器 API 和反射。
  • 使用 Runtime.getRuntime().exec 作为 root

I am aware that by default Java does not have the so-called eval (what I pronounce as "evil") method. This sounds like a bad thing—knowing you do not have something which so many others do. But even worse seems being notified that you can't have it.

My question is: What is solid reasoning behind it? I mean, Google'ing this just returns a massive amount of old data and bogus reasons—even if there is an answer that I'm looking for, I can't filter it from people who are just throwing generic tag-words around.

I'm not interested in answers that are telling me how to get around that; I can do that myself:

Using Bean Scripting Framework (BSF)

File sample.py (in py folder) contents:

def factorial(n): 
    return reduce(lambda x, y:x * y, range(1, n + 1))

And Java code:

ScriptEngine engine = new ScriptEngineManager().getEngineByName("jython");
engine.eval(new FileReader("py" + java.io.File.separator + "sample.py"));
System.out.println(engine.eval("factorial(932)"));

Using designed bridges like JLink

example

This is equivalent to:

String expr = "N[Integrate[E^(2 y^5)/(2 x^3), {x, 4, 7}, {y, 2, 3}]]";
System.out.println(MM.Eval(expr));
//Output: 1.5187560850359461*^206 + 4.2210685420287355*^190*I

Other methods

  • Using Dijkstras shunting-yard algorithm or alike and writing an expression evaluator from scratch.
  • Using complex regex and string manipulations with delegates and HashMultimaps.
  • Using Java Expressions Library
  • Using Java Expression Language
  • Using JRE compliant scripting language like BeanShell.
  • Using the Java Assembler and approach below or direct bytecode manipulation like Javaassist.
  • Using the Java Compiler API and reflections.
  • Using Runtime.getRuntime().exec as root

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

猥︴琐丶欲为 2024-10-28 22:32:14

“eval”仅在脚本语言中可用,因为它使用运行其余代码的相同解释器;在此类语言中,该功能是免费的并且集成良好,因为在脚本环境中,如果您运行字符串或“真实”函数,则几乎没有什么区别。

在编译语言中,添加“eval”意味着捆绑整个编译器 - 这将违背编译的目的。我所知道的编译语言(即使是动态语言,如 ActionScrip3)都没有 eval。

顺便说一句,在 Java 中评估最简单的方法就是您忘记提到的方法:JRE 1.6 附带 Javascript 引擎,因此您可以用两行代码评估任何 Javascript。你甚至可以辩称你的问题的前提是错误的。 Java 1.6 捆绑了一个非常先进的表达式计算器

"eval" is only available in scripting languages, because it uses the same interpreter that runs the rest of the code; in such languages the feature is free and well integrated, as in scripting environment it makes little difference if you run a string or a "real" function.

In copiled languages, adding "eval" would mean bundling the whole compiler - which would defy the purpose of compiling. No compiled language I know (even dynamic ones, like ActionScrip3) has eval.

Incidentally, the easiest way to eval in Java is the one you forgot to mention: JRE 1.6 comes with Javascript engine, so you can eval any Javascript in two lines of code. You could even argue that the presuposition of your question is false. Java 1.6 bundles a very advanced expression evaluator.

自由范儿 2024-10-28 22:32:14

正如 Daniel 指出的,Java 中的评估解决方案至少面临一个限制。例如,php eval 执行代码就好像它是周围方法的一部分,可以完全访问局部变量,这在标准 java 中是不可能做到的。如果没有此功能,评估替代方案需要更多的工作和冗长的内容,这使得它们对于“快速”和“简单”解决方案的吸引力大大降低。

eval() 主要是解释语言的一部分,其中局部变量的名称和代码结构(范围)在运行时可用,从而可以“插入”新代码。 Java 字节码不再包含此信息,使得 eval() 替代方案无法映射对局部变量的访问。 (注意:我忽略调试信息,因为没有程序应该依赖它,并且它可能不存在)

示例

int i = 0;
eval("i = 1");
System.out.println(i);

Java 所需的伪代码

context.put("i",new Integer(0));
eval(context,"i = 1");
System.out.println(context.get("i"));

这对于 eval 中使用的一个变量来说看起来不错,在更长的方法中尝试 10 次,您会得到 20用于变量访问的附加行以及一个或另一个运行时错误(如果您忘记了)。

As Daniel points out there is at least one limitation that eval-solutions face in java. The php eval for example executes the code as if it was part of the surrounding method with complete access to local variables, this is not possible to do in standard java. Without this feature eval alternatives require a lot more work and verbosity, which makes them a lot less attractive for "quick" and "easy" solutions.

eval() is mostly part of interpreted languages where the names of local variables and code structure(scopes) are available at runtime, making it possible to "insert" new code. Java bytecode no longer contains this information leaving eval() alternatives unable to map access to local variables. (Note: I ignore debug information as no program should rely on it and it may not be present)

An example

int i = 0;
eval("i = 1");
System.out.println(i);

required pseudocode for java

context.put("i",new Integer(0));
eval(context,"i = 1");
System.out.println(context.get("i"));

This looks nice for one variable used in the eval, try it for 10 in a longer method and you get 20 additional lines for variable access and the one or other runtime error if you forget one.

策马西风 2024-10-28 22:32:14

因为任意 Java 表达式的求值取决于它的上下文、变量作用域等。

如果您需要某种变量表达式,只需使用脚本框架,然后 badamm!你有很多不同类型的表达式评估。只需将 JavaScript 等一种作为默认值,就有您的 eval()!

Java 是企业级的,您不会局限于一种选择。

Because evaluation of arbitrary Java expressions depends on the context of it, of variable scopes etc.

If you need some kind of variable expression, just use the scripting framework, and badamm! you have lots of different kinds of expression evaluation. Just take one kind like JavaScript as a default, and there is your eval()!

Enterprisy as Java is, you are not constrained to one choice.

煞人兵器 2024-10-28 22:32:14

但更糟糕的是,你似乎被告知你不能拥有它。

我认为您误解了这些文章(大部分)的内容。显然,有很多方法可以在Java应用程序中进行表达式求值。它们并不总是可用,但至少其中一些已经存在很长时间了。

我认为人们想说的是,表达式求值不能作为本机提供(即作为 Java 或标准库的固有部分)并且不太可能被添加,原因有很多。例如:

  • Native eval 如果用在错误的地方就会产生严重的安全问题。 (对于其他语言也是如此;例如,您不应该在 Javascript 中使用 eval 来读取 JSON,因为它可能是向用户浏览器注入不良内容的途径。)
  • 本机 eval 会存在严重的性能问题,与编译的 Java 代码相比。我们谈论的是慢 100 到 10,000 倍,具体取决于实现技术和“已编译” eval 表达式的缓存量。
  • 原生评估会引入一系列可靠性问题……就像类型转换和反射的过度使用/误用一样。
  • Native eval“不是Java”。 Java 被设计为主要是静态的编程语言。

当然......

  • 还有其他方法可以做到这一点,包括您列出的所有实现方法。 Java SE 平台的目的并不是提供任何人可能想要的所有可能的库。 (JRE 下载量已经足够大了。)

出于这些原因,可能还有其他原因,Java 语言设计者决定不在 Java SE 中原生支持表达式求值。 (即便如此,一些表达式支持已正式纳入 Java EE;例如,以 JSP 表达式语言的形式。这些类位于 javax.el 包中...或 javax.servlet .jsp.el 适用于较旧/已弃用的版本。)

But even worse seems being notified that you can't have it.

I think you are misunderstanding what (most of) those articles are saying. Clearly, there are many ways to do expression evaluation in a Java application. They haven't always been available, but at least some of them have been around for a long time.

I think what people are trying to say is that expression evaluation is not available as native (i.e. as an intrinsic part of Java or the standard libraries) and is unlikely to be added for a number of good reasons. For example:

  • Native eval would have significant security issues if used in the wrong place. (And it does for other languages; e.g. you shouldn't use eval in Javascript to read JSON because it can be a route for injecting bad stuff into the user's browser.)
  • Native eval would have significant performance issues, compared with compiled Java code. We are talking of 100 to 10,000 times slower, depending on the implementation techniques and the amount of caching of "compiled" eval expressions.
  • Native eval would introduce a whole stack of reliability issues ... much as overuse / misuse of type casting and reflection to.
  • Native eval is "not Java". Java is designed to be a primarily static programming language.

and of course ...

  • There are other ways to do this, including all of the implementation approaches that you listed. The Java SE platform is not in the business of providing every possible library that anyone could possibly want. (JRE downloads are big enough already.)

For these reasons, and probably others as well, the Java language designers have decided not to support expression evaluation natively in Java SE. (Even so, some expression support has officially made it into Java EE; e.g. in the form of JSP Expression Language. The classes are in the javax.el package ... or javax.servlet.jsp.el for an older / deprecated version.)

国粹 2024-10-28 22:32:14

我认为您已经将解决​​方案放在您的答案中 - 将 BeanShell jar 与您的应用程序捆绑在一起(或者游说将其包含在 JRE 中),并且您拥有了 Java 表达式计算器。不过,它仍然需要输入变量的绑定。

(我更好奇的是:这样的脚本/表达式的沙箱如何工作?我不希望我的网络用户在我的服务器中执行危险代码。)

I think you already put the solution to your answer - bundle the BeanShell jar with your application (or lobby for it to be included in the JRE sometime), and you have your Java expression evaluator. It will still need a Binding of the input variables, though.

(What I'm more curious about: How does sandboxing of such a script/expression work? I don't want my web users to execute dangerous code in my server.)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文