定义Map

def map1 = [
    keyToStr  : "value",
    keyToVar  : varRef,
    keyToList : [e1, e2, e3],
    keyToMap  : [
        key1  : 1,
        key2  : 2,
        key3  : 3
    ],
    (XxxClass.simpleName) : "xxx"      // Expression as Key
]

groovysh

// groovysh 使用 untyped 类型。而使用 def 或 明确类型的 变量,将不会暴露到 shell 环境中。
// 可以通过以下方式改变
:set interpreterMode

// 列出所有本机IP地址。
Enumeration e = NetworkInterface.getNetworkInterfaces();
while(e.hasMoreElements())
{
    NetworkInterface n = (NetworkInterface) e.nextElement();
    Enumeration ee = n.getInetAddresses();
    while (ee.hasMoreElements())
    {
        InetAddress i = (InetAddress) ee.nextElement();
        if(i.isAnyLocalAddress() || i.isLoopbackAddress() || i.isMulticastAddress() || i.getAddress().length != 4){
            continue;
        }
        System.out.println(i.getHostAddress());
    }
}

Design patterns in Groovy

see here

DSL

  1. 空格用来分隔方法调用,如果没有参数,则需要明确加上 ()

     // equivalent to: turn(left).then(right)
     turn left then right
    
     // equivalent to: select(all).unique().from(names)
     select all unique() from names
    
  2. 逗号用来分隔单个方法调用时的参数列表

     // equivalent to: paint(wall).with(red, green).and(yellow)
     paint wall with red, green and yellow
    
     // with named parameters too
     // equivalent to: check(that: margarita).tastes(good)
     check that: margarita tastes good
    

注解中无法使用常量

see groovy issue 2178. 只能先在Java中定义常量,再在groovy中引用。

添加新方法

为类添加静态方法

per-Instance 与 其 metaClass 存储在 org.codehaus.groovy.reflection.ClassInfo#perInstanceMetaClassMap 中,参见348行。

Object.metaClass.static.hi = {println "hi,"+it}
Object.metaClass.static.hi = {String str-> println "hi-" + str}
Object.metaClass.static.getMyClassName = { delegate.name }
Integer.hi()    // 无参函数
Integer.hi("a") // 含参函数
println Integer.myClassName // java.lang.Integer

为类添加预定义的新方法、属性

// 追加方法
Object.metaClass.hi = {println "hi,"+it}
Object.metaClass.hi = {String str-> println "hi-" + str}
// 追加属性:命名要求是 getXxx
Object.metaClass.getMyClassName { delegate.getClass().getName() }
1.hi()    // 无参函数
1.hi("a") // 含参函数
println 1.myClassName

为类添加动态发现的新方法、属性

TODO

为特定的实例添加方法

def a = "a"
def b = "b"

a.metaClass.hi{println "hi,$delegate"}

a.hi()
b.hi()  // ERROR

为特定的实例添加新属性

TODO

FIXME

// export JAVA_OPTS="-Xmx12M -XX:-UseGCOverheadLimit -Xloggc:gc.log -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=oom.dump.hprof"
// 1. 限定最大堆内存大小,
// 2. 不抛出 java.lang.OutOfMemoryError: GC overhead limit exceeded
// 3. 记录gc日志
// 4. 内存溢出时,dump出内存
// groovy GMain.groovy
// 该测试用例大约在 250 次的时候出错
class GMain {

    static main(args) {
        def r = new Random()

        500.times { i ->
            def str = r.nextInt().toString()
            def list = []
            10.times {
                list << r.nextInt()
            }
            str.metaClass.bbb = {list}
            println "${i} : ${str}.bbb = ${str.bbb()}"
        }
    }
}

自定义 AST

参考: Developing AST transformations

全局 transformation

当代码编译时记性,参与全局变换的,需要在相应的jar包中 META-INF/services/org.codehaus.groovy.transform.ASTTransformation 进行配置。实现类必须实现 org.codehaus.groovy.transform.ASTTransformation 接口并提供无参数构造函数。 可以通过 ASTTransformationCustomizer 配置,可以避免将 自定义筛选用的 annotation 类放到单独的一个jar包中。

本地 transformation

必须在 org.codehaus.groovy.control.CompilePhase 中定义的9个阶段完成

AST 类型检查

请参考 这里

Closure#rehydrate()@DelegatesTo

Gradle 中相关的静态类型检查请参考

  1. gradle-api-3.4.1.jar!/META-INF/services/org.gradle.initialization.GradleApiSpecProvider
  2. org.gradle.initialization.DefaultGradleApiSpecProvider
  3. DefaultServiceLocator
  4. GradleApiSpecProvider

  5. GradleApiSpecAggregator 通过 DefaultServiceLocator

GlobalScopeServices#createClassLoaderRegistry()
-> new DefaultClassLoaderRegistry()
-> GradleApiSpecAggregator#aggregate()
-> DefaultServiceLocator#implementationsOf() // 从 `META-INF/services/` 找出实现了 GradleApiSpecProvider 的服务类,
                                             // 目前只有一个 ——  DefaultGradleApiSpecProvider
-> DefaultGradleApiSpecProvider

Type checking extensions

see here

  1. org.codehaus.groovy.transform.stc.GroovyTypeCheckingExtensionSupport