为什么Groovy不能处理花括号?

4
在Android Studio(以及可能的任何)Gradle文件中,以下代码有效:
task build {
}

即使只有一处微小的更改,也可能导致整个系统崩溃:

task build
{
}

这个问题在其他帖子中也提到过,但是在修复构建文件的上下文中。我的问题是为什么Gradle/Groovy不能处理两种括号样式?许多其他语言都可以很好地处理,那么这里有什么大不了的呢?

2个回答

4

实际上,错误信息中已经包含了所有信息:

build file '.../build.gradle': 80: Ambiguous expression could be a parameterless closure expression, an isolated open code block, or it may continue a previous statement;
     solution: Add an explicit parameter list, e.g. {it -> ...}, or force it to be treated as an open block by giving it a label, e.g. L:{...}, and also either remove the previous newline, or add an explicit semicolon ';' @ line 80, column 1.

由于Groovy语法糖使得最后一个参数为lambda的方法看起来像语言结构一样,因此以下代码块:

task build {}
task build2(type: Copy) {}

等于它们更常见的形式:

task build({})
task build(type: Copy, {})

现在,你不希望使用大括号来界定普通的代码块,而是 Groovy lambda,该 lambda 应作为参数传递给 build 方法。

但从外观上看,当你在两行之间加入一个换行符时,Groovy 无法确定它是否真正作为前一行中传递给方法的 lambda,还是一个不相关的代码块。这就是错误消息中描述的歧义所在。

按照错误消息中的建议,您也可以使用以下语法,而不是使用转义换行符的语法:

task build
{ -> 
}

最后,用于调用动态方法(在您的示例中命名为build)的task关键字不是Groovy特定的,而是Gradle DSL功能。

但是 doLast \n{ ... 是可以工作的... 我认为这更多是在解析任务定义时的DSL问题。 - tim_yates
有趣...这个错误以及它发生的原因在我看来很纯粹的Groovy,可以看到第2122行的方法(短类是怎么了,哈!)和相关的groovydoc:https://github.com/groovy/groovy-core/blob/master/src/main/org/codehaus/groovy/antlr/groovy.g。可能是因为doLast是一个静态定义的方法(当然不是static),它总是需要一个单一的闭包参数(https://docs.gradle.org/3.5/dsl/org.gradle.api.Task.html),而在gradle中跟随`task`关键字调用的方法都是动态的,所以无法确定它们应该具有什么参数? - Michal M
这绝对是一个“build”任务DSL问题/实现选择,因为我使用的其他“关键词”都没有这个问题(buildscript、plugins、release、artifactory等)。 - Bungles
是的。buildscriptplugins等都是调用静态定义的方法,接受一个单一的Closure类型参数。知道没有参数就不完整,所以没有歧义。task有点特殊,因为它有一个Action参数,这个参数是通过调用动态方法(这里是build,但名称完全是任意的,没有区别)创建的,其确切的参数列表未定义,因此存在歧义。将动态方法调用强制转换为Action实例的方式是Gradle DSL的复杂性。 - Michal M

2
如果有人在阅读此文时想知道,解决方法相当简单。
task build \
{
}

我只是在想“为什么”...

Gradle的构建脚本是DSL,而不是严格的Groovy脚本或类文件。我觉得这可能是个bug,但是将其发布到问题跟踪器上可能值得一试。 - tim_yates

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接