诊断Java代码:Repl提供交互式评价

通常,在运行 Java 程序时,必须使用 String[] 输入参数将参数传入,从 main() 方法运行程序。但是如果程序仍在调试中,那么,这可能会成为一项繁重的任务。本月,Eric Allen 讨论对程序中的表达式和语句进行交互式评价的优点,并且提供几个帮助您处理这一任务的 Java repl(“read-eval-print-loop”工具,“读取-评价-打印-循环”工具)。他还讨论交互式评价如何帮助构建 GUI 和探索新的 API。

多数程序都包含很多方法,这些方法分布在为数众多的类中。毫无疑问,从程序的 main 入口点测试所有这些方法,即使不是不可能的,也是很困难的。

这就是单元测试之所以有用的原因。许多程序员和软件设计人员(包括我自己)强调单元测试在编写健壮的的软件时是有用的。但是如果您想能够以一种交互性更好的方式访问程序中的各种元素时,则可能要折衷一下。

当确实要这样做时,为每个结果编写、编译并运行新的单元测试就会很快变成一件繁重的事情。我发现,当不能预知给予特定输入后程序将表现出什么行为时(例如:在 AI 程序中可能就会出现这种情况),尤其如此。

那么,该怎么办呢?

不要为小改动着急

为了作一个类比,请考虑用通常情况下进行编译的语言(例如:Java 或 C++)编程和用在更多情况下进行解释的语言(Python 或 Scheme)编程之间的差异。

在编译型语言中,每个编写/测试/调试循环都必须包含编译这一额外的步骤,这可能是一个单调乏味的经历,尤其是对于一些小改动而言。这可能会使我们得出结论说,解释型语言更流畅,因而也更易于修改。(这种灵活性是有代价的:解释型语言通常更少对代码执行静态检查,例如类型检查。)

正如有时候我们可能想对程序作个改动,但不必经历重编译的麻烦一样,我们也可能想检查程序中的一些元素,但不必例行公事般在套件中添加一个新的单元测试。当确实想这样做时,拥有传统上称为“读取-评价-打印-循环”(即 repl)的工具可能会有所帮助。

repl是一个基于文本的工具,它以表达式作为输入,在特定程序的上下文中进行评端,然后显示结果。接着,它等待获得另一个表达式作为输入,然后重复这些操作。这样的工具源于类似 Lisp 的语言,但它们也能在更新的语言(例如:Python)中使用。

repl 在 Java 编程中的好处

这样的工具并非仅仅在这些语言中是有用的。Java 程序员也可以从使用 repl 中获得好处,不只是调试方面,在其它方面也可以。

构建 GUI

当组装一个 GUI 时,有许多组件需要布置和连接。当构造 GUI 时,您肯定会碰到以下这些事情:

组件之间将以不可见的方式相互作用。

在运行 GUI 之前写出其所有代码是相当费时的。

一旦您看到了 GUI 的实际视觉效果,不可避免地,您将会想更改 GUI 的某些方面。

这个问题的一种常见的“解决方案”是使用图形化的 GUI 构建工具,例如那些包含在 JBuilder、Forte 和其它 IDE 中的 GUI 构建工具。我个人不喜欢这种办法 — 您很难知道这个工具会给您生成什么样的 Java 代码,您也不可能在修改所生成的代码时不冒丧失与 GUI 构建工具的兼容性的风险(事实上,有些 IDE 强行禁止您修改机器生成代码的任何部分)。

此外,许多这类 GUI 构建工具在生成 Java 代码时都使用专用 GUI 库,因而限制了 GUI 的兼容性。

我发现使用 repl 来构建 GUI 要容易得多。我完全可以交互地定义每一个 GUI 组件,然后依次显示它。我能够立刻修正任何不喜欢的东西。然后,我可以与之交互并将这些组件粘贴到程序中。

探索新 API

使用 Java 语言编程最大的优点之一是,有数量庞大的 API 可以使用,它们可以与一切事物 — 从数据库到 Web 服务到电视 — 对接。不过需要花些时间学习 API 的语义。

通常,Javadocs 不会对 API 的行为的每个方面都作出明确说明。对付这种窘境的办法是直接测试 API,使用 repl 可以使测试快得多 — 只要输入一个方法调用看看其结果就行了!

一个额外的好处是,使用 repl 测试 API 还强化了大多数程序员的主要行为 — 我们倾向于在实践中取得最好的学习效果。

Java 编程中可用的 repl

那么,如果 repl 有这么多优点,下一个问题显然是 Java 语言可以使用哪些 repl?

Jython

Jython(以前的名称是 JPython)是 Python 的 Java 编程语言实现(包含一个 repl)(经认证,是 100% 纯 Java 的)。实际上,它把 Python 编译成(有点复杂)Java 源代码或直接编译成字节码。

本着 Python 的精神,人们尝试了各种办法以提供 Java 和 Jython 之间的无缝互操作性。Jython 让您可以访问所有的 Java 标准库,就好像您是自然地使用 Java 语言进行编程一样,您也可以访问现有的 Java 类文件。所以您不仅可以将 repl 用来与标准库一起工作,还可以与已经编译成字节码的您自己的 Java(或 Jython)类一起工作。

使用 Jython repl 时的一个重要注意事项是您在写 Python 表达式,而不是 Java 表达式。其积极的一面是您得到了 Python 在句法方面简明而又优美的优点。

例如,假如我想构造一个新的散列表,这个表将 a 映射到 1、 b 映射到 2, c 映射到 3。使用 Jython 我所要写的只是:

>>> h = {‘a’:1, ‘b’:2, ‘c’:3}

解释器在每一个新的输入行前显示 >>> 。

当探索新的 GUI 设计时,Jython 句法也有很多优点。举个例子来说,可以将 GUI 元素的各个域指定为构造函数的关键字参数,就像这样:

>>> from javax.swing import *>>> f = JFrame(visible=1)

这个示例说明了 Jython 和 Java 语言的其它一些差异:

导入语句的句法有很大不同。

整型被用来取代了布尔型(1 是真,0 是假)。

这里是另一个示例,示例中 Jython 代码为您节省了一些输入,给 GUI 元素添加了动作侦听器(action listener)。通常,这样的侦听器通过使用命令模式(Command Pattern)被指定为匿名内部类的实例。在 Python(和其它许多“脚本”语言)中,这样的命令可以通过使用交互式函数定义更简洁地加以指定。例如,让我们在上述的交互式会话的基础上给 JButton 添加一个简单的动作侦听器:

>>> def listener(event):...  print 'thank you'>>>

这是 Jython 中的函数定义的一个示例。为了让我们知道它什么时候需要一个语句以便继续进行,解释器在下一行打印省略号(代替脱字符)。这个函数只需要一个参数,并会将“thank you”打印到标准输出。我们可以将它用作动作侦听器,如下:

>>> panel = JPanel()>>> panel.add(JButton('press me', actionPerformed=listener))>>> f.getContentPane().add(panel)>>> f.pack()

现在,我们将有一个显示在屏幕上的窗口,这个窗口有一个标有“press me”的按钮,按下这个按钮后将打印“thank you”到标准输出。想像一下,如果使用 Java 代码的话,这将需要多少语句啊。

当然,缺点也是有的。例如:

您失去了静态类型检查(尽管按理说静态检查在 repl 中没有多少价值)。

因为您输入到 repl 中的表达式不是 Java 代码,所以您无法在转换它们之前将表达式从 repl 中复制和粘贴到您的程序中。

当与 Java 代码一起使用 Jython 时,您得承担在脑海中同时处理两种语言的额外脑力负担(尽管有些人认为这样做很好玩)。

DynamicJava

Java 可以使用的另一个 repl 是 DynamicJava,一个真正基于 Java(呃,基本上是对的)的开放源代码的工具,它有一些不同之处:

repl 语言允许您不必在声明变量时指定变量的静态类型。

您不必在语句末尾添加一个分号。解释器也会返回(随意地) null 作为语句的评价结果。(如果语句根本不返回值,情况会好得多。)

不限制您从 repl 内访问对象的私有字段。

对于初级 Java 程序员,这些不同之处是很重要的,因为它们可能会使他们感到很迷惑。更有经验的程序员可能会乐于见到其中一些宽松的约束。无论如何,DynamicJava 都是一个健壮的、非常有用的软件产品(而且它是免费的,这是有帮助的)。

repl 总结

希望本部分已经指出并演示了一种主要工具,它能让您交互地对 Java 程序中的表达式和语句进行评价,而不陷入重编译的泥沼 — 这种工具就是“读取-评价-打印-循环”即 repl 工具。我们还演示了 repl 如何体现它在构建 GUI 中的重要性,或者当您只是想快速地检查大量可以使用的 Java API 时,它又是如何体现它的重要性的。

当你下定决心准备出发时,最困难的时刻就已经过去了。那么,出发吧。

诊断Java代码:Repl提供交互式评价

相关文章:

你感兴趣的文章:

标签云: