为什么人们说Java不能有表达式评估器?

时间:2011-03-12 20:30:43

标签: algorithm language-agnostic reflection java

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

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

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

使用Bean Scripting Framework(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 shunting-yard算法或类似的,并从头开始编写表达式求值程序。
  • 对委托和HashMultimaps使用复杂的正则表达式和字符串操作。
  • 使用Java表达式库
  • 使用Java表达式语言
  • 使用类似JRE的脚本语言,如BeanShell。
  • 使用Java汇编程序和下面的方法或直接字节码操作,如Javaassist。
  • 使用Java编译器API和反思。
  • 以root身份使用Runtime.getRuntime().exec

5 个答案:

答案 0 :(得分:6)

“eval”仅在脚本语言中可用,因为它使用运行其余代码的相同解释器;在这些语言中,这个特性是免费的,并且很好地集成了,就像在脚本环境中一样,如果你运行一个字符串或一个“真正的”函数,它就没什么区别。

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

顺便提一下,在Java中评估eval的最简单方法就是你忘了提到:JRE 1.6附带了Javascript引擎,所以你可以用两行代码来评估任何Javascript。你甚至可以争辩说你的问题的预设是错误的。 Java 1.6捆绑了一个非常高级的表达式评估程序

答案 1 :(得分:5)

正如丹尼尔指出的那样,eval-solutions在java中至少存在一个限制。例如,php eval执行代码就好像它是周围方法的一部分,可以完全访问局部变量,这在标准java中是不可能的。如果没有这个功能,eval替代品需要更多的工作和冗长,这使得它们对“快速”和“简单”解决方案的吸引力大大降低。

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个额外的变量访问行和一个或其他运行时错误。

答案 2 :(得分:2)

因为任意Java表达式的评估取决于它的上下文,变量范围等。

如果你需要某种变量表达式,只需使用脚本框架和badamm!你有很多不同种类的表达评价。只需要像JavaScript一样默认,并且有你的eval()!

像Java一样企业化,你并不局限于一种选择。

答案 3 :(得分:2)

  

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

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

我认为人们试图说的是,表达式评估不能作为本机(即作为Java的固有部分或标准库)提供,并且不太可能被添加很好的理由。例如:

  • 如果在错误的地方使用,本机eval会出现严重的安全问题。 (并且它适用于其他语言;例如,您不应在Javascript中使用eval来读取JSON,因为它可能是将不良内容注入用户浏览器的路径。)
  • 与编译的Java代码相比,Native eval会出现严重的性能问题。我们谈论的速度要慢100到10,000倍,具体取决于实现技术和“编译”eval表达式的缓存量。
  • Native eval会引入一大堆可靠性问题......就像过度使用/误用类型转换和反射一样。
  • Native eval是“not Java”。 Java旨在成为一种主要的静态编程语言。

当然......

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

出于这些原因,也可能是其他原因,Java语言设计者决定不在Java SE中本地支持表达式评估。 (即便如此,一些表达式支持已经正式将其转换为Java EE;例如以JSP表达式语言的形式。这些类在javax.el包中...或javax.servlet.jsp.el用于较旧/不推荐的版本。)

答案 4 :(得分:0)

我认为您已经将解决​​方案提供给您的答案 - 将BeanShell jar与您的应用程序捆绑在一起(或者随后将它包含在JRE中),并且您拥有Java表达式评估程序。但是,它仍然需要输入变量的绑定。

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