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

Mar*_*gus 11 java language-agnostic reflection algorithm

我知道默认情况下Java没有所谓的eval(我称之为"邪恶")方法.这听起来像是一件坏事 - 知道你没有其他许多人做的事情.但更糟糕的是,似乎被告知你无法拥有它.

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

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

使用Bean Scripting Framework(BSF)

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

def factorial(n): 
    return reduce(lambda x, y:x * y, range(1, n + 1))
Run Code Online (Sandbox Code Playgroud)

和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)"));
Run Code Online (Sandbox Code Playgroud)

使用像JLink这样的设计桥梁

例

这相当于:

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
Run Code Online (Sandbox Code Playgroud)

其他方法

  • 使用Dijkstras shunting-yard算法或类似的,并从头开始编写表达式求值程序.
  • 使用复杂的正则表达式和字符串操作与委托和HashMultimaps.
  • 使用Java表达式库
  • 使用Java表达式语言
  • 使用类似JRE的脚本语言,如BeanShell.
  • 使用Java Assembler和下面的方法或直接字节码操作,如Javaassist.
  • 使用Java Compiler API和思考.
  • Runtime.getRuntime().exec以root身份使用

fdr*_*ger 6

"eval"仅在脚本语言中可用,因为它使用运行其余代码的相同解释器; 在这些语言中,该功能是免费的并且很好地集成,如在脚本环境中,如果您运行字符串或"真实"功能,它几乎没有区别.

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

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

  • "我所知道的编译语言没有eval" - Lisp. (5认同)

jos*_*efx 5

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

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

一个例子

int i = 0;
eval("i = 1");
System.out.println(i);
Run Code Online (Sandbox Code Playgroud)

java的必需伪代码

context.put("i",new Integer(0));
eval(context,"i = 1");
System.out.println(context.get("i"));
Run Code Online (Sandbox Code Playgroud)

这对于eval中使用的一个变量看起来很好,在较长的方法中尝试10,并且如果忘记了一个变量,则会获得20个额外的行用于变量访问和一个或其他运行时错误.