Groovy闭包rehydrate设置DELEGATE_ONLY策略时委托方法调用报错
问题产生的核心原因
你遇到的异常来自两个Groovy闭包的常见认知误区:
rehydrate()方法不会修改调用它的原闭包对象,而是会返回一个配置了新delegate、owner、thisObject的全新闭包副本。你的代码里调用cl.rehydrate(new Bar(), this, this)后没有接收返回值,后续执行的cl还是原来未配置的闭包,根本没有绑定Bar实例作为委托。@DelegatesTo注解仅用于给IDE、静态代码检查工具提供类型提示,不会修改闭包运行时的实际解析策略。闭包默认的解析策略是Closure.OWNER_FIRST,也就是优先从闭包的owner(这里是Foo类实例)查找方法,找不到自然会抛出Foo.greeting()不存在的异常。
直接赋值delegate能正常运行的原因
你直接给原闭包的delegate属性赋值Bar实例时,修改的是即将执行的原闭包本身的配置。虽然你没有显式修改解析策略,默认OWNER_FIRST逻辑下会先在Foo类找greeting方法,找不到就回退到delegate(Bar实例)查找,刚好Bar类定义了greeting方法,所以能正常执行——这属于默认策略下的回退命中,并没有真正实现你预期的DELEGATE_ONLY效果。
正确实现方案
要严格按照DELEGATE_ONLY策略委托给Bar实例,有两种合规写法:
- 推荐写法:使用
rehydrate()生成闭包副本,避免污染原闭包配置def bar(@DelegatesTo(value = Bar, strategy = Closure.DELEGATE_ONLY) Closure cl) { // 接收rehydrate返回的配置完成的新闭包 Closure configuredCl = cl.rehydrate(new Bar(), this, this) // 显式设置运行时解析策略,注解不会自动完成该配置 configuredCl.resolveStrategy = Closure.DELEGATE_ONLY // 调用配置完成的闭包副本 configuredCl.call() } - 简单场景写法:直接修改原闭包配置(注意闭包复用时会有配置污染风险)
def bar(@DelegatesTo(value = Bar, strategy = Closure.DELEGATE_ONLY) Closure cl) { cl.delegate = new Bar() cl.resolveStrategy = Closure.DELEGATE_ONLY cl.call() }
注意:直接修改传入闭包的delegate和解析策略会产生副作用,如果闭包在外部被复用、或者在多线程场景下使用,会出现配置互相覆盖的问题,生产环境优先选择
rehydrate()生成副本的实现方式。
内容的提问来源于stack exchange,提问作者Derrops
相关产品推荐
相关产品推荐

