Groovy 省略括号的函数调用

Groovy function call omiting the parentheses

根据gradle documentation/section 13.5.2我们可以在方法调用中省略括号:

Parentheses are optional for method calls.

但是当我们尝试应用java插件时似乎不起作用。如果脚本包含以下行:

apply [plugin: 'java']

我们会得到错误:

 Maybe something should be set in parentheses or a comma is missing?
 @ line 1, column 8.
     apply [plugin: 'java']
            ^

但是,如果我们将这个 Map-文字放在括号中,它会正常工作。

apply([plugin: 'java'])

所以当参数是 Map 时我们不能省略括号,可以吗?

如规范所说,在没有歧义的情况下可以省略括号。我怀疑在这种情况下会出现歧义,因为不带括号的语句看起来很像数组索引语法,并且解析器无法确定您是在调用名为 'apply' 的方法还是试图对名为 [= 的数组执行某些操作18=].

就个人而言,这就是为什么我总是倾向于使用括号 - 如果解析器无法解决问题,我相信阅读代码的其他程序员也不会。

虽然 Groovy 中数组或映射的常用语法确实使用方括号(例如,对于空方括号,您通常分别编写 [][:]), 如果相同的括号符号跟在标识符 之后,则将其解释为索引运算符。然后 Groovy 尝试将 apply 解释为 Gradle 项目的 属性,即使它不存在。 Groovy 作为一种动态语言,允许我们动态定义属性,因此并不总能在编译时判断 属性 是否存在。虽然这是否是一个好的设计值得怀疑,Gradle 的 ExtraPropertiesExtension very much makes use of this dynamic nature in Groovy for convenience. If you prefer more strict typing, I suggest you try the Kotlin DSL, which has much less of this kind of problems (I do not think it is completely gone, as we can still explicitly declare variables as dynamic type).

另一方面,DSL 的一个目的是简洁并删除无用的仪式。这是 Groovy 擅长的一件事,因为如果您需要传递给方法或闭包的唯一参数只是一个数组或映射,您可以省略各种括号:apply plugin: 'java'。 (任何比这更复杂的东西对于 DSL 来说都是有问题的。)

这就是为什么我认为一直在任何地方添加括号(例如 apply([plugin: 'java']))并不是 build scripts whose code is supposed to look declarative and to support that, very DSL like, at least not if you use Groovy, which was designed exactly for that purpose. Never using a language feature, even where it might have an advantage, is what lead to books like Douglas Crockford's JavaScript: The Good Parts, where the author recommends we always use semicolons, and that is one of the most argued practices on the Internet, e.g. disagreed by the JavaScript Standard Style 的正确方法。我个人认为了解您使用的语言更为重要,例如,知道何时需要分号,通常可以生成质量最好的代码。诚然,分号示例可能不是减少仪式的有力示例。但是仅仅因为一种语言特性可能被滥用,并不意味着我们应该禁止它的使用。一把菜刀并不坏,因为我们可以用它伤人。 Kotlin 最受评判的功能也是如此。

虽然现在我们得到了the plugins DSL, which is the preferred way of loading plugins (even if plugins are put in buildSrc, which will just need some metadata definition),这个问题可能仍然相关,原因如下:

  • 以编程方式应用插件,例如subprojects { apply plugin: 'java' },
  • 包括使用类似语法的自定义构建脚本:apply from: 'myscript.gradle'
  • 也许如果出于某种原因您只能通过其 class 名称引用插件。

因此问题仍然有效。