black Corona typewriter on brown wood planks

Apache Groovy:强大的脚本语言

Apache Groovy 对于脚本编写来说是一大胜利。代码比 Java 更简洁,这使得…
首页 » 博客 » Apache Groovy:强大的脚本语言

Apache Groovy 是一种强大、简洁且富有表现力的语言,可用于各种脚本编写任务。本系列文章旨在展示它可以为您做更多的事情。(如果您尚未安装 Groovy,请阅读简介。)

首先,用 Java 编写一个命令行程序,将文件复制到标准输出

1  import java.io.File;
2  import java.io.FileReader;
3  import java.io.BufferedReader;
4  import java.io.FileNotFoundException;
5  import java.io.IOException;
6  public class Groovy01 {
7  public static void main(String[] args) {
8    if (args.length != 1) {
9      System.err.println("Usage: java Groovy01 input-file-name");
10     System.exit(0);
11   }
12 File inputFile = new File(args[0]);
13 try {
14   BufferedReader reader = new BufferedReader(new FileReader(inputFile));
15   String inputLine;
16   while ((inputLine = reader.readLine()) != null) {
17     System.out.println(inputLine);
18   }
19   reader.close();
20   } catch (FileNotFoundException fnfe) {
21     System.out.println("FileNotFound exception");
22     fnfe.printStackTrace();
23   } catch (IOException ioe) {
24     System.out.println("IO exception");
25     ioe.printStackTrace();
26   }
27  }
28 }

这是一个非常简单的 Java 程序

  • 第一到五行导入执行该任务所需的各种 Java 类。
  • 第六到二十八行定义了主类 Groovy01(Java 程序始终是一个类)。
  • 第七到二十七行定义了主方法(Java 程序始终提供一个 main() 方法,该方法由操作系统调用并执行程序的预期工作。这是从 C 编程语言和早期的 Unix 继承的标准)。
  • 第八到十一行确保提供了一个参数,该参数旨在成为要读取的文件。
  • 第十二到二十六行包含主要的读取-写入逻辑,并提供 FileReader 构造函数(捕获 FileNotFoundException)和 BufferedReader 构造函数(捕获 IOException)所需的 trycatch 错误处理。
  • 第十五到十八行是逐行执行文件复制的行。

在脚本中读取文件行可能需要大量工作,因为它需要设置很多东西并弄清楚如何使用它。实际上,脚本中唯一与读取文件没有直接关系的行是第 17 行

17 System.out.println(inputLine);

这是我在 Groovy 中执行此操作的方式

1 if (args.length != 1) {
2   System.err.println "Usage: groovy Groovy01.groovy input-file"
3   System.exit(0)
4 }
5 new File(args[0]).withReader { reader ->
6   reader.eachLine { line ->
7   println line
8   }
9 }

显然,如果您使用类似的 Groovy “脚本”,您就不必担心那么多了!

  • 第一到四行确保提供了一个参数。
  • 第五到九行包含主要的读取-写入逻辑。

如果您从头开始,通常不需要 import 语句。不需要定义主类。不需要定义 main() 方法。从另一方面来说,不需要 trycatch 错误处理步骤。所有这些都是可选的 — 如果程序员不包含它们,则会“隐式地”包含合理的默认值。如果脚本用户提供的参数无法解释为可读文件,则脚本将失败,并出现与 Java 程序情况相同的 FileNotFoundExceptionIOException。一些 Groovy 程序员对此特别满意,因为他们通常实现的 trycatch 处理只是转储异常信息。

请注意 withReader { reader -> … }eachLine { line -> … } 构造,它们都是方法调用,其最后一个参数是 Groovy 类 Closure 的实例。

Groovy 闭包是匿名代码块,可以接受参数、与包含范围交互并返回值。它们类似于 Java lambda,但有一些优势,特别是与包含范围交互的能力。由于闭包从一开始就存在于 Groovy 中,因此许多类提供了与闭包一起使用的方法,并且该语言为闭包提供了语法支持。

以最简单的例子为例:reader 有一个 eachLine() 方法,该方法循环遍历 reader 定义的输入中的每一行,并调用其闭包参数。它将读取的行作为参数传递给该闭包。在这种情况下,闭包的主体只是在标准输出上打印该行。但是可能会发生更复杂的事情。例如,如果目标是计算行数和字符总数,则第五到九行可能会替换为

5  int nLines = 0, nChars = 0
6  new File(args[0]).withReader { reader ->
7  reader.eachLine { line ->
8    nLines++
9    nChars += line.size()
10   }
11 }
12 println "# of lines = $nLines # of chars = $nChars"

在这里,您可以看到第五行中定义的行计数器和字符计数器,在第八行和第九行中累积,并在第 12 行的文件末尾打印。

withReader() 方法中发生的一件好事是输入流在退出时会自动关闭。

结论

在本系列中,我不会总是比较 Groovy 和 Java 的等价物,也不会为 Groovy 的简洁性而摇旗呐喊。但在这种情况下,这很有意义,因为我正在谈论一种用于脚本编写的“样板”。对于需要读取带有换行符的文本文件并对每行信息执行某些操作的任务,您可以看到“样板”看起来像这样

1  if (args.length != 1) {
2    System.err.println "Usage: groovy Groovy01.groovy input-file"
3    System.exit(0)
4  }
5  // set up any accumulators
6  new File(args[0]).withReader { reader ->
7    reader.eachLine { line ->
8    // analyze the line and accumulate stuff
9   }
10  }
11  // summarize the accumulators

这就是 Groovy 脚本编写如此出色的原因 — 只需在第五、八和十一行中插入相关的任务细节,即可开始使用。

这篇文章是关于 Apache Groovy 系列文章的一部分,请继续关注更多内容。

照片由 Patrick Fore 拍摄于 Unsplash

作者

如果您喜欢这篇文章,您可能也会喜欢这些