启动的委托字段在闭包中变为空,Groovy

Initiated delegated field became null in a closure, Groovy

Groovy 版本:2.4.3 JVM:1.8.0_60 供应商:Oracle 公司 OS:Mac OS X

import groovy.transform.*

@Canonical
class A {
    def f() {
        map = [:] //map is not null
        3.times {
            assert map != null // failed, map is null?!
        }
    }

    @Delegate
    Map map
}
new A().f()

当我调用 f() 时,断言失败,这意味着 map 为 null。但是如果我删除注解'@Delegate',就不会有任何问题。或者如果断言不在闭包中,也没问题。我的问题是为什么委托字段在闭包内外表现不同?如果是因为闭包中的 map 不是 class A 中的同一个对象,为什么它在注释删除后仍然有效?

import groovy.transform.*

@Canonical
class A {
    def f() {
        map = [:]
        3.times {
            assert map != null // No problem, map is not null
        }
    }

    Map map
}
new A().f()

或者

import groovy.transform.*

@Canonical
class A {
    def f() {
        map = [:]
        assert map != null //no problem too
    }

    @Delegate
    Map map
}
new A().f()

当你有 @Delegate 注释时,你的 class 本质上变成了 java.util.Map 的实现,将其调用委托给它的字段 map。断言闭包中对 map 的引用被视为 this.getAt('map') 调用,而不是对 map 字段的引用。由于没有值映射到键 map,断言失败。

也许这样更清楚一点:

import groovy.transform.*

@Canonical
class A {
    def f() {
        map = [map:'not null'] // delegated map has key 'map'
        3.times {
            assert map == 'not null'
        }
    }

    @Delegate
    Map map
}
new A().f()

奇怪之处主要在于断言是在闭包内完成的,闭包中有 resolve strategy 指示如何解析引用,其中包括元 class 的使用。移除闭包,@Delegate 注释不再产生影响。

import groovy.transform.*

@Canonical
class A {
    def f() {
        assert map != null
    }

    @Delegate
    Map map = [:]
}
new A().f()