groovy - Groovy 'with' 方法的意外行为 - 变量分配静默失败
问题描述
我有以下代码:
import groovy.transform.ToString
@ToString(includeNames = true)
class Simple {
String creditPoints
}
Simple simple = new Simple()
simple.with {
creditPoints : "288"
}
println simple
显然,我在这里犯了一个错误creditPoints : "288"
。它应该是creditPoints = "288"
。
我预计 Groovy 在运行时会失败,说我犯了一个错误,我应该使用creditPoints = "288"
它,但显然它没有。
既然它没有失败,那么 Groovy 对我创建的闭包做了什么?
解决方案
从 Groovy 编译器的角度来看,闭包代码中没有错误。编译器将creditPoints : "288"
其视为标记语句,这是 Groovy 编程语言中的合法构造。正如文档所说,标签语句不会向生成的字节码添加任何内容,但它可以用于例如 AST 转换(Spock 框架大量使用它)。
如果您将代码更准确地格式化为标签语句用例,它将变得更加清晰易懂,例如
class Simple {
String creditPoints
static void main(String[] args) {
Simple simple = new Simple()
simple.with {
creditPoints:
"288"
}
println simple
}
}
(注意:我将您的脚本放在main
方法主体中,以便在下一节中向您展示其字节码表示。)
现在我们知道编译器是如何看待这个构造的,让我们来看看最终的字节码是什么样子的。为此,我们将反编译该.class
文件(我为此使用 IntelliJ IDEA - 您只需.class
在 IDEA 中打开文件,它就会为您反编译):
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
import groovy.lang.Closure;
import groovy.lang.GroovyObject;
import groovy.lang.MetaClass;
import groovy.transform.ToString;
import org.codehaus.groovy.runtime.DefaultGroovyMethods;
import org.codehaus.groovy.runtime.GeneratedClosure;
import org.codehaus.groovy.runtime.InvokerHelper;
@ToString
public class Simple implements GroovyObject {
private String creditPoints;
public Simple() {
MetaClass var1 = this.$getStaticMetaClass();
this.metaClass = var1;
}
public static void main(String... args) {
Simple simple = new Simple();
class _main_closure1 extends Closure implements GeneratedClosure {
public _main_closure1(Object _outerInstance, Object _thisObject) {
super(_outerInstance, _thisObject);
}
public Object doCall(Object it) {
return "288";
}
public Object call(Object args) {
return this.doCall(args);
}
public Object call() {
return this.doCall((Object)null);
}
public Object doCall() {
return this.doCall((Object)null);
}
}
DefaultGroovyMethods.with(simple, new _main_closure1(Simple.class, Simple.class));
DefaultGroovyMethods.println(Simple.class, simple);
Object var10000 = null;
}
public String toString() {
StringBuilder _result = new StringBuilder();
Boolean $toStringFirst = Boolean.TRUE;
_result.append("Simple(");
if ($toStringFirst == null ? false : $toStringFirst) {
Boolean var3 = Boolean.FALSE;
} else {
_result.append(", ");
}
if (this.getCreditPoints() == this) {
_result.append("(this)");
} else {
_result.append(InvokerHelper.toString(this.getCreditPoints()));
}
_result.append(")");
return _result.toString();
}
public String getCreditPoints() {
return this.creditPoints;
}
public void setCreditPoints(String var1) {
this.creditPoints = var1;
}
}
如您所见,与with
方法一起使用的闭包表示为内部_main_closure1
类。这个类扩展Closure
了类,它实现了GeneratedClosure
接口。闭包的主体封装在public Object doCall(Object it)
方法中。该方法只返回"288"
字符串,这是预期的——闭包的最后一条语句默认成为返回语句。生成的字节码中没有标签语句,这也是预期的,因为标签在CANONICALIZATION
Groovy 编译器阶段被剥离。
推荐阅读
- html - 如何并排放置锚标签?
- php - 如何按另一个表的速率对数据进行排序
- tensorflow - 如何使用 fp16 和 fp32 训练模型进行推理?
- java - 第 1 行的跟踪无效?如何在java中修复行检查器
- symfony - Symfony 3.4 - 如何将 Swiftmailer 传递给事件监听器?
- javascript - 即使路径正确也找不到图像模块
- elasticsearch - 有没有办法将属性类型定义为 ElasticSearch 数组数据类型中的关键字?
- python - 在数据框中添加列
- docusignapi - 将自定义 url 查询参数线程化到 PowerForm 回调
- javascript - JavaScript Canvas 如何描边特殊文本