# 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}")}
````


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://yangsx95.gitbook.io/notes/programming-language/jvm-language/groovy.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
