> For the complete documentation index, see [llms.txt](https://yangsx95.gitbook.io/notes/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://yangsx95.gitbook.io/notes/programming-language/jvm-language/groovy.md).

# Groovy

Groovy基于Java语言，兼容Java语法，他其实就是一种较为系统的java语法糖，既可以作为面向对象编程语言，也可以作为脚本语言。

## 基础语法

### Groovy Script 和 Groovy Object

作为脚本时，Groovy脚本文件中的所有脚本命令将会生成一个Java Class，并继承`groovy.lang.Script`类。

比如如下脚本 `Hello.groovy`：

```groovy
print("hello world")
```

将会生成一个与脚本文件名称相同的类，这里是`Hello.class`：

```java
import groovy.lang.Binding;
import groovy.lang.Script;
import org.codehaus.groovy.runtime.InvokerHelper;
import org.codehaus.groovy.runtime.callsite.CallSite;

// 继承Script，并提供一个 main 方法
public class Demo01 extends Script {
    public Demo01() {
        CallSite[] var1 = $getCallSiteArray();
        super();
    }

    public Demo01(Binding context) {
        CallSite[] var2 = $getCallSiteArray();
        super(context);
    }

    public static void main(String... args) {
        CallSite[] var1 = $getCallSiteArray();
        var1[0].callStatic(InvokerHelper.class, Demo01.class, args);
    }

    public Object run() {
        CallSite[] var1 = $getCallSiteArray();
        return var1[1].callCurrent(this, "hello world");
    }
}
```

不仅如此，在一个Groovy文件中可以混合类定义与脚本定义，但是脚本名不可以与脚本中定义的类名相同，因为脚本类也会生成Class，Class是不可以重名的。

如下代码是一个混合脚本，除了`Hello.class`，还会生成一个`Person.class`：

```groovy
import groovy.lang.GroovyObject;
import groovy.lang.MetaClass;
import groovy.transform.Generated;
import groovy.transform.Internal;
import java.beans.Transient;
import org.codehaus.groovy.runtime.callsite.CallSite;

// Groovy 的class 默认实现 GroovyObject 接口
public class Person implements GroovyObject {
    @Generated
    public Person() {
        CallSite[] var1 = $getCallSiteArray();
        super();
        MetaClass var2 = this.$getStaticMetaClass();
        this.metaClass = var2;
    }

    @Generated
    @Internal
    @Transient
    public MetaClass getMetaClass() {
        MetaClass var10000 = this.metaClass;
        if (var10000 != null) {
            return var10000;
        } else {
            this.metaClass = this.$getStaticMetaClass();
            return this.metaClass;
        }
    }

    @Generated
    @Internal
    public void setMetaClass(MetaClass var1) {
        this.metaClass = var1;
    }
}

```

### 使用def定义变量

Groovy提供关键字def用于定义变量，他是一种动态类型机制，也就是说，在运行时才会去校验数据类型；而静态类型是在编译时对数据类型进行校验检查。

```groovy
def s = "hello"
```

### 分号可以用换行替换

```groovy
print("hello world")
print("hello world")
```

### 类、成员默认全是public的

Java类、以及类中的成员，默认修饰级别为default，而Groovy中默认则是public的：

```groovy
public class Person{}
```

```groovy
class Person{}
```

上面两个代码是相同的。

### 类中的属性会自动添加getter和setter

```groovy
class Person {
    def name
    def age
    def hobby
}

def p = new Person()
println(p.getHobby())
```

### 对象的属性操作

```groovy
// 创建对象
def p = new Person()

// 使用属性名直接给成员赋值
p.name = "张三"
p["age"] = 20
// 使用setter方法赋值
p.setHobby("打篮球")

// 使用属性名读取属性值
println(p.name)
println(p["age"])
// 使用getter方法获取
println(p.getHobby())
```

> 无论是哪儿种方式，实际上最终仍然是调用的getter和setter

### 创建对象使用构造器指定属性值

```groovy
def p = new Person(name: "张三", age: 12)
```

### 方法的定义

声明方法时，参数类型可省略：

```groovy
String getHelloStr(String name) {
    return "hello " + name
}
println getHelloStr("张三")

// 方法的返回值与参数类型可以使用def代替
def getHelloStr1(def name) {
    return "hello " + name
}
println getHelloStr1("张三")

// 方法的参数类型可以不写
def getHelloStr2(name) {
    return "hello " + name
}
println getHelloStr2("张三")

// renturn 关键字可以省略
def getHelloStr3(name) {
    "hello ${name}"
}
println getHelloStr3("张三")
```

### 方法的调用可以省略()

当不引起歧义的时候，方法调用的小括号可以省略，下面两行代码是相同的：

```groovy
println "hello world"
println("hello world")
```

### 在Groovy中没有基本类型，字面量可以直接调用包装类型方法

```groovy
def one = 1.longValue()
```

### 三种字符串表示

* 单引号：字符串常量，不具有运算能力，会被解析为 `java.lang.String`
* 双引号：可引用变量`${}`，具有运算能力，会被解析为 `GStringImpl`
* 三引号：支持换行，会被解析为`java.lang.String`

```groovy
def name = '张三'

def sayhello = "你好，${name}"
// 在不引起歧义的情况下可省略大括号
sayHello = "你好, $name";

def str = '''
多行字符串
支持换行
**不支持**引用变量 ${name}
'''
```

### 自动类型转换

在需要的时候，类型之间会自动发生类型转换。

### 控制语句

控制语句基本与java相同。

## 闭包

定义：开放的、匿名的代码块，他可以接收参数，也可以有返回值。闭包还可以引用其周围作用域中声明的变量。

语法：

````groovy
{
    [closureParameters ->] statements
}

使用闭包需要：

1. 将闭包赋值给一个变量
2. 使用 `变量名()` 或者 `变量名.call()` 调用闭包

> 开发中通常将闭包作为方法参数调用

示例：

```groovy
def helloWorld = {
    println "hello world"
}
helloWorld()

def hello = {
    name -> println "hello $name"
}
hello.call("张三")

def run(Closure closure) {
    closure();
}
run({println("run!!!")})

def caculate(num1, num2, Closure closure) {
    closure(num1, num2)
}
caculate(10, 15, {k, v -> println("$k + $v = ${k+v}")})

// 如果闭包方法的最后一个参数，那么调用时，可以将闭包这个参数写在方法外面
caculate(10, 15) {k, v -> println("$k + $v = ${k+v}")}
````
