学习 Groovy

包括Groovy基本知识和闭包(clousure)相关

Posted by Haiming on October 10, 2019

Groovy 是一门基于 JVM 的动态语言,既可以面向对象编程,又可以用作纯粹的脚本语言。在学习 Java 语法的情况下会很容易学习,下面就是 Groovy 和 Java 语法的不同之处的学习。

学习教程:https://www.kancloud.cn/kancloud/learnxinyminutes/58931

Groovy基础

1. 与Java的异同

Groovy可以作为 Java 的脚本语言使用,因为其运行于 JVM 的特性,其可以使用其他Java库。

下面是其和Java 相异的地方:

  1. Groovy 之中包括了 Java 之中不支持的静态和动态类型(使用关键字 def)
  2. 提供 List 和 Maps 的原生语法
  3. 原生支持 正则表达式,字符串内嵌表达式,Null条件运算符,自动空指针检查
  4. Groovy 源代码文件可以作为未编译的脚本执行。Groovy脚本在执行之前完成解析,编译和生成。

Groovy之中动态类型的特性十分方便,可以将不同的基本类型添加到一个 collections 之中。

1.1 Java 和 Groovy 的类的区别

  1. 不需要public 修饰符。Groovy 的默认访问修饰符就是 public。
  2. 不需要类型说明。Groovy 也不关心变量和方法参数的具体类型。
  3. 不需要 getter/setter 方法。连IDE自动生成都不需要。当然,也支持写 getter 和 setter
  4. 不需要构造函数。注意,此处的“不需要”,前提条件是其操作比较常规,例如只是给一些参数进行赋值,而不是说不可以有。在需要独特业务的情况下,可以自己进行业务结合的构造函数。
  5. 不需要 return。当然,Groovy不可能智能到你想要什么都知道。实际上,在需要 return 的情况下,如果省略 return,那么Groovy 会自动将整个程序的最后一句作为返回值。如果不是这种情况,那么需要自己指定返回值
  6. 不需要()。在 Groovy 之中,Groovy 的方法调用可以省略(),但是构造函数除外。
  7. Groovy 不需要分号作为结尾。

2. 字符串处理

相对于 Java 而言,Groovy 对于字符串的操作简单很多。

在 Groovy之中,单引号和双引号都可以定义一个字符串常量,不同的是,单引号标记的是纯粹的字符串常量,但是双引号的字符串可以对字符串里面的表达式做运算。

class Attribute {
    static void main(args){
        println "Hello world"
        def str1='single quota '
        def str2="double quota "
        println(str1+str1.getClass().name)
        println(str2+str2.getClass().name)
    }
}

程序运行之后可以看到输出为:

Hello world
single quota java.lang.String
double quota java.lang.String

但是不可以对单引号之中的字符串的表达式做运算,下面举个例子:

class Attribute {
    static void main(args){
        def name="John"
        println 'single quota can not do operation:$name'
        println "double quota can do operation:$name"
        }
}

其输出为:

single quota can not do operation:$name
double quota can do operation:John

可见双引号之中的字符串可以做一些操作,但是单引号之中的不可以。

双引号可以直接进行表达式计算的功能很好用,可以直接做很多计算,不用 Java 之中的 + 进行一点点的串联了。嵌套的规则是,一个美元符号紧跟着一对花括号,花括号里放表达式,比如name,name,{1+1}等等,只有一个变量的时候可以省略花括号,比如$name。

3.  集合

Groovy 完全兼容了 Java 的集合,并且进行了扩展,在扩展之中,声明,迭代,查找集合元素等等都会变得非常容易。常见的集合有 List,Set,Map和Queue,这边只介绍 List 和 Map。

3.1 List

在Java 之中我们使用 List, 例如 ArrayList, 需要New 一个 ArrayList 的类,在 Groovy 之中直接定义就好:

class Attribute {
    static void main(args){
        def numList=[1,2,3,4,5]
        println numList.getClass()
        }
}

输出为:

class java.util.ArrayList
Just for a en

可见,在定义一个变量并且对其赋值之后,其会直接转换成一个 List. 这里面的输出就是一个 ArrayList.

在定义好集合之后,怎么访问集合之中的元素?Groovy 之中,可以直接使用下标来对某个集合进行操作.

class Attribute {
    static void main(args){
        def numList =[1,2,3,4,5,6];
        println numList.getClass().name
        println numList[1]//访问第二个元素
        println numList[-1]//访问最后一个元素
        println numList[-2]//访问倒数第二个元素
        println numList[1..3]//访问第二个到第四个元素
        }
}

对于迭代的元素,还有迭代的方法 each 进行操作.该方法接受一个 闭包 (closure) 作为其参数.

class Attribute {
    static void main(args){
        def numList =[1,2,3,4,5,6];
        println numList.getClass().name
        println numList[1]//访问第二个元素
        println numList[-1]//访问最后一个元素
        println numList[-2]//访问倒数第二个元素
        println numList[1..3]//访问第二个到第四个元素

        numList.each {
            println it
        }
        }
}

在这里,it 就是后来迭代的元素.

3.2 Map

Map 之中的值是一个 K:V 键值对

class Attribute {
    static void main(args) {
    def map1=['width':1024,'height':2048]
        println(map1.getClass().name)

        println(map1['width'])
        println(map1.height)
    }
}

上面的两种方式都可以取出 key-value 的 value.

下面示例是 map 的迭代:

class Attribute {
    static void main(args) {
    def map1=['width':1024,'height':2048]
        println(map1.getClass().name)

        println(map1['width'])
        println(map1.height)

        map1.each{
            println("Key:${it.key};Value:${it.value}")
        }
    }
}

输出:

java.util.LinkedHashMap
1024
2048
Key:width;Value:1024
Key:height;Value:2048

Groovy 对于集合,还提供了诸如 collect, find, findAll 等等方法.

4. 方法

4.1 括号是可以省略的

之前的代码之中,对于方法的调用也省略了括号.此处就不再叙述下面的区别.

4.2 return 可以省略

之前也提到过,return 可以直接省略,省略的话,是直接将最后一句的值作为 return

4.3 代码块可以作为参数传递

在 Groovy 之中,代码块可以作为参数传递.但是结合上面的特性,最后的闭包就会比较优雅.以集合的each 方法为例:

//基于死板的写法其实是这样
numList.each({println it})
//我们格式化一下,是不是好看一些
numList.each({
    println it
})
//好看一些,Groovy规定,如果方法的最后一个参数是闭包,可以放到方法外面
numList.each(){
    println it
}
//然后方法可以省略,就变成我们经常看到的啦
numList.each {
    println it
}

5. Java Bean

在Groovy 之中,上面提及到我们并不需要 getter/setter 来进行属性的赋值等等,例如:

class Attribute {
    static void main(args) {
    Person p = new Person()

        println("Name is ${p.name}")
        p.name="Shawn"
        println("Name is ${p.name}")

        
    }
}
class Person{
    private String name;
}

其输出为:

Name is null
Name is Shawn

可以看到,在第一个1个${p.name}之中,其得到的值为 null. 在第二个之中,经过赋值,其得到的值就是 Shawn.

在 Groovy 之中,可以不定义成员变量,而是直接使用 getter/setter 方法,一样可以被识别成属性并且被访问.

class Attribute {
    static void main(args) {
    Person p = new Person()

        println("Name is ${p.name}")
        p.name="Shawn"
        println("Name is ${p.name}")
        println("Age is ${p.age}")

    }
}
class Person{
    private String name;

    public int getAge(){
        12
    }
}

得到的结果:

Name is null
Name is Shawn
Age is 12

但是如果在这种情况下,我们不可以进行设置值. 因为我们只定义了 getAge()方法,而没定义其 setter 方法.同样的,如果定义了相应的 getter/setter 方法的话,也会让使用的人认为其是一个相应的属性.

6. 闭包

闭包是 Groovy 的一个非常重要的特性.

问过同事,之前自己认为闭包和正常声明一个方法区别不大,实际上在辨析过后发现,其都是用来实现某种功能,和 java的匿名函数类似,虽说没有什么只有闭包没有正常代码(比如for 循环等) 做不到的,但是闭包可以显著减少代码量.那么下面就对于闭包做一定的梳理:

6.1 初始闭包

前面讲过,闭包就是一段代码块,下面以each 为例子,用闭包的 it 变量来将整个基础的闭包实现:

class Attribute {
    static void main(args) {
        def clos={println("Hello world")}
        println("Executing closure")
        clos()
    }
}

输出为:

Executing closure
Hello world

可以看到其 def clos 之中的代码在 clos() 的地方才被执行.

6.2 向闭包传递参数

闭包之中也可以传递参数,像这样:

class Attribute {
    static void main(args) {
    def sum={a,b -> println(a+b)}
        sum(2,4)
    }
}

输出为:

6

6.3 闭包传递参数列表之外变量

当然,闭包也可以使用参数列表之外的变量,类似于:

class Attribute {
    static void main(args) {
        def x=5
        def multiplyBy={num -> num*x}
        println multiplyBy(10)
    }
}

输出:

50