Groovy常用编程知识点简明教程

Posted lovesqcc

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Groovy常用编程知识点简明教程相关的知识,希望对你有一定的参考价值。

概述

Groovy 是一门基于JVM的动态类型语言,可以与 Java 平台无缝融合(与Java代码一样编译成字节码)。 使用 Groovy ,可以增强 Java 应用的灵活性和可扩展性,提升开发效率。使用 Groovy + Spock 组合也更容易编写简洁易懂的单测。熟练使用 Groovy 编程是很有益处的。

本文针对已有一定java开发经验,想要快速熟悉和使用Groovy语言的童鞋。本文会跳过变量、作用域等基础通用的编程内容,重点给出与Groovy相关的实用性的知识点。


动态变量定义

Java 里的变量与函数定义对于 Groovy 基本适用。不加修饰符,通常默认是 public 的。Groovy 还有一种动态类型的定义符 def ,可以定义动态类型的变量。如下代码所示,chameleon 先后被赋值为一个整型、字符串、闭包。


        def chameleon = 0
        println "i am a ${chameleon.getClass().name}"
        chameleon = 'haha changed'
        println "now i am ${chameleon}"
        chameleon = { a1, a2, op -> op(a1,a2) }
        println "now i have changed to ${chameleon.getClass().name}"
        println "power(2,10) = ${chameleon(2,10, {bg,p->Math.pow(bg,p)})}"


动态函数调用

Groovy 支持简便的动态函数调用,使用 "${funcName}" 来引用函数名进行调用即可。


    def static swim() {
        println "swim"
    }
    def static football() {
        println "football"
    }

    ['swim', 'football'].each {
            "${it}"()
        }


字符串

字符串是编程中最常用的对象。关于字符串,需要掌握如下三点:

  • 创建字符串;

  • 变量引用

  • 正则匹配与捕获

Groovy字符串的诸多方法,可以在网上搜索,这里不再赘述。


创建字符串

可以使用单引号、双引号或三引号创建字符串。 单引号可以创建普通字符串,消除转义; 双引号可以创建含变量引用的字符串;三引号可以创建多行字符串。

注意: 字符串是不可变的。如果要创建可变的字符序列,使用 StringBuilder 类。


        def name = 'qin'
        def intro = "i am $name"
        def intro2 = 'i am $name'
        println "intro=$intro, intro2=$intro2"
        def multilines = '''
            There will be one day when
            i am proud of who i am.
            Because i believe my heart.
        '''
        println multilines


变量引用

变量引用是Groovy对字符串模板的重要支持,可以方便地格式化输出文本。变量引用必须围在一对双引号里(双引号可以是两个或三个‘)。变量引用也可以作用于方法调用。比如:


println "intro=$intro, size = ${intro.length()} intro2=$intro2 size=${intro2.length()}"


正则表达式

Groovy 使用 ~ 创建正则表达式,其类型是 java.util.regex.Pattern 。使用 =~ 或 ==~作为匹配操作符,其中, =~ 是部分匹配, ==~ 是全匹配。=~ 操作符的结果是一个 Matcher 类,含有被匹配的所有字符串列表。 正则匹配示例如下:


       def multilines = '''There will be one day when
            i am proud of who i am.
            Because i believe my heart.'''
        println multilines
        println multilines =~ "\\w+\\s+" ? 'matched' : 'not matched'
        println multilines ==~ "\\w+\\s+" ? 'matched' : 'not matched'
        def matched = multilines =~ /w+s+/
        matched.each {
            println "matched: $it"
        }
        def isAllMatched = multilines ==~ /(?m)^(([a-zA-z]+s+)+([a-zA-z]+[.
]+)s*)+$/
        println isAllMatched ? 'All matched' : 'not matched'
        def matchedCaptured = multilines =~ /(?:[a-zA-z]+s+)+(?:[a-zA-z]+[.
]+)/
        matchedCaptured.each {
            println "matchedCaptured: $it"
        }

有几点说明下:

  • 正则表达式可以使用单引号、双引号或双斜线//围起来。使用双斜线时,可以对反斜杠转义,双引号下 "w+" 可以写成 /w+/ ;

  • 多行匹配使用 ?m ,忽略捕获分组使用 ?:

  • 编写正确的正则表达式,我没有固定经验可循,只有不断尝试,从最简单的字符串匹配,依次编写子表达式匹配字符串里每个子部分,然后将子表达式组合起来即可。组合的方式有顺序、+,*,?, [] 等。

  • 匹配正确后,可以使用 each 遍历匹配的字符串。

关于正则表达式的基础知识,可参阅:“正则表达式基础知识”


闭包

A closure is a function with variables bound to a context or environment in which it executes.

闭包就是一个闭合代码块,可以引用传入的变量。之所以首先谈groovy的闭包,是因为Groovy的闭包大大简化了容器的遍历,使代码更加简洁优雅。闭包在Groovy编程中几乎无处不在。

含有闭包的函数

闭包在Groovy 的类型是 groovy.lang.Closure , 如下代码创建了一个使用 closure 来处理 Range [1,2,...,num] 的函数:

def static funcWithClosure(int num, final Closure closure) {
        (1..num).collect { closure(it) }
    }

使用该函数的代码如下:

println funcWithClosure(5, {x -> x*x})

如果闭包是最后一个参数,还可以写成:

println funcWithClosure(5) { x -> x * 2 }

遍历容器

如下代码所示,分别创建了一个Map, List 和 Range, 然后使用 each 方法遍历。一个闭合代码块,加上一个遍历变量,清晰简单。注意到,如果是一个单循环遍历,可以直接用 it 表示;如果是 Map 遍历,使用 key, value 二元组即可。


class GroovyBasics {
    static void main(args) {
        def map = ["me":["name": 'qin', "age": 28], "lover":["name": 'ni', "age": 25]]
        map.each {
            key, value -> println(key+"="+value)
        }

        def alist = [1,3,5,7,9]
        alist.each {
            println(it)
        }
        (1..10).each { println(it) }


        def persons = [new Person(["name": 'qin', "age": 28]), new Person(["name": 'ni', "age": 25])]
        println persons.collect { it.name }
        println persons.find { it.age >=28 }.name
    }
}

再看一段代码:


        (1..10).groupBy { it % 3 == 0 } .each {
            key, value -> println(key.toString()+"="+value)
        }

将 [1,10]之间的数按照是否被3除尽分组得到如下结果,使用链式调用连接的两个闭包实现,非常简明。

false=[1, 2, 4, 5, 7, 8, 10]

true=[3, 6, 9]


柯里化

《函数柯里化(Currying)示例》 一文中讲述了函数柯里化的概念及Scala示例。Groovy 也提供了 curry 函数来支持 Curry.

闭包可以轻易地实现模板方法模式,可用于资源释放,构建 DSL,超轻量级框架 等,使代码更加优雅简洁。

容器

任何一门编程语言中,容器的使用是重中之重。灵活使用Map,List 可以让代码实现更简洁。


Map

Map 的创建非常简单,中括号 [] 里面包含一系列的 key:value 即可。跟Python的语法一样简洁。通过 def map = [key:value] 创建的Map类型是 java.util.LinkedHashMap 。 访问Map中的元素,可以用 map[key] , map.key (key含特殊字符不可以), map.‘key‘ , 或 map.get(key), map.getOrDefault(key) 。

Map 的基本方法有 each, collect, find, findAll ,分别用来做最通用的遍历、收集器、查询过滤操作。


        println map['me']
        println map.lover
        println map.getOrDefault("nonexist", [:])
        println map.collect { it.value['name'] }
        println map.findAll { it.value.age <=25 }


List

List 的创建非常简单,中括号里包含逗号分隔的一系列元素。跟大多数语言里的数组创建一样。通过def list = [1,2,3] 创建的List 类型是 java.util.ArrayList。


下标访问

使用下标访问List中的元素很灵活。可以使用 list[i], list.get(i), 可以使用区间下标,负数下标。如下所示:


        println "first: " + alist.get(0)
        println "second: " + alist[1]
        println alist[2..4]
        println alist[-1]


常用方法

list 也有非常丰富的方法来操作其中的元素。each, eachWithIndex, find, findAll, collect, groupBy, join, flatten, inject 等,这些参数都是一个闭包。

eachWithIndex可以按照下标遍历列表元素; find, findAll 在列表中查询满足条件的第一个元素或所有元素列表;collect 可以将列表元素转换成另一个列表;groupBy可以根据某种条件对列表元素分组;join可以连接列表元素成字符串;flatten 可以将嵌套列表打平成一维列表;inject 类似 reduce 方法。


      alist.eachWithIndex {
            int entry, int i ->
                println "index=$i, item=$entry"
        }
        println alist.findAll {
            it % 3 == 0
        }
        println alist.collect { it * it }
        println '[' + alist.join(",") + ']'
        println alist.inject(1) { n1, n2 -> n1 * n2 }
        println ([[1,2,4], [1,3,9], [1,4,16]].flatten())
        println ([[[1,5,10], [1,6,12]], []].flatten())  


对象

定义一个Groovy类很简单,几乎只要指定属性名即可。def 默认为属性添加 getter/setter 方法。


class Person {
    def name
    def age
    def address
}
class Address {
    def detail
}

然后可以基于Map来创建对象。一般使用 List 来存储多个对象,然后通过 List 方法来操作对象列表。访问对象的属性用 object.attrName 即可。如下代码所示:


      def persons = [new Person(["name": 'qin', "age": 28, "address": new Address(["detail": "Hangzhou"])]), new Person(["name": 'ni', "age": 25])]
        println persons.collect { "${it.name} live in ${it.address?.getDetail()}" }
        println persons.find { it.age >=28 }.name

这里使用了 ?. 操作符对对象做非空判断。一般用于访问嵌套对象中某个子对象的属性而该子对象可能不存在的情况。 ?. 必须放置在可能不存在的子对象的后面,表示对这个对象的存在性“表示怀疑”。这样就免除了写 if-else 判断的繁琐,尤其在多层嵌套对象的时候更为方便。另外也可以看到,变量引用也可以作用于对象的属性和方法。

Groovy对象也支持动态方法调用。只要使用 objectRef."${methodName}"(args) 即可。


        persons[0].class.declaredMethods.findAll {
            it.parameterCount == 0
        }.each {
            println "method = ${it.name}, callValue = ${persons[0]."${it.name}"()}"
        }


文件读写

Groovy对文件读写提供了非常方便的支持,不需要像Java那样new一堆的实现细节类(应该有一个Facade类),还需要谨慎地打开和关闭文件。

读文件的代码如下:分别是读取整个文本、读取每一行并进行指定处理、将读取的每行存入列表。


File file = new File("README.md")
println file.text
file.eachLine("UTF-8") {
     println it
}
println file.readLines()

写文件的代码如下(使用了with语法):


new File("/tmp/result.txt").withPrintWriter { printWriter ->
            printWriter.println('The first content of file')
        }

是如下写法的简化版:


        def printWriter = new File("/tmp/result2.txt").newPrintWriter()
        printWriter.write('The first content of file')
        printWriter.flush()
        printWriter.close()

遍历目录主要是使用过滤器和闭包来遍历匹配的所有文件或目录并进行处理。注意,eachFileRecurse 会递归遍历该目录下所有子目录的所有文件,而 eachFileMatch 则只遍历该目录下的文件而不包含递归子目录的文件。


      path.eachFileRecurse(FileType.FILES) {
            def filename = it.name
            if (filename =~ ~/.*.groovy$/) {
                println filename
            }
        }


        path.eachFileMatch(~/.*..*/) {
            println it.name
        }


        path.eachDirRecurse {
            def filename = it.canonicalPath
            if (filename =~ ~/.*groovy$/) {
                println filename
            }
        }

以上是关于Groovy常用编程知识点简明教程的主要内容,如果未能解决你的问题,请参考以下文章

Groovy入门

最全最详细publiccms常用的代码片段

最全最详细publiccms其他常用代码片段(内容站点)

在 Groovy 中将字符串 XML 片段转换为文档节点

Groovy入门教程

译文:18个实用的JavaScript代码片段,助你快速处理日常编程任务