Groovy介绍


  Groovy是一种动态语言,对它的定义是:Groovy是在java平台上的,具有像Python、Ruby和smalltalk语言特性的灵活动态语言,Groovy保证了这些特性像java语言一样被java开发者使用。说白了就是让写java程序变的像写脚本一样简单,Groovy内部会将其编译成java class,然后放到JVM上执行。

  此外Groovy是一种DSL(Domain Specific Language),是针对某个领域所涉及出来的一个特定的语言,因为有了领域的限制,要解决的问题就被划定了范围,所以语言不需要复杂,就可以具有精确的表达能力,使用起来非常方便。更具体的来说:使用java或者C++也能够实现相同的功能,但是会产生大量繁琐的代码并导致大量的领域知识被隐藏在通用语言构造中(比如:for循环、if条件等等)。(关于DSL感兴趣的,可以自己Google,可以深入学习下怎么定义自己的DSL语言)。

1 语法基础


1)注释

 Groovy的单行注释、多行注释、文档注释和java一样。只有一种特殊的单行注释需要留意:

#!/usr/bin/env groovy
println "Hello from the shebang line"

这种注释在脚本中经常见到,一般都是固定写法。

2)关键字

  Groovy有如下的关键字:

  as、assert、break、case、catch、class、const、continue、def、default、do、else、enum、extends、false、finally、for、goto、if、implements、import、in、instanceof、interface、new、null、package、return、super、switch、this、throw、throws、trait、true、try、while。

看到了吧,其实它和java的关键字区别不大,其中不一样的关键字是:

  • def用来定义一个变量,定义变量时可以不指定类型。
  • goto在java中是保留关键字
  • assert常用在C或者C++中
  • trait是一组可重用的方法和字段,可以将他们混入一个或者多个类中,一个类可以同时拥有多个trait而不需要使用多重继承。
  • 其他关键字可能有的略有不同,在使用过程中可以再了解。

3)标识符

  • 普通标识符和常用语言类似,只能以字母、美元符号、下划线开始,不能以数字开始
  • 引用标识符出现在点后的表达式中,比如:
def map = [:]
//引用标示符中出现空格也是对的
map."an identifier with a space and double quotes" = "ALLOWED"
//引用标示符中出现横线也是对的
map.'with-dash-signs-and-single-quotes' = "ALLOWED" assert map."an identifier with a space and double quotes" == "ALLOWED"
assert map.'with-dash-signs-and-single-quotes' == "ALLOWED"

Groovy的所有字符串都可以当作引用标识符定义,如下:

//如下类型字符串作为引用标识符都是对的
map.'single quote'
map."double quote"
map.'''triple single quote''' //三个引号的,可以换行显示
map."""triple double quote"""
map./slashy string/
map.$/dollar slashy string/$ //稍微特殊的GString,也是对的
def firstname = "Homer"
map."Simson-${firstname}" = "Homer Simson" assert map.'Simson-Homer' == "Homer Simson"

4)字符及字符串

  • 单引号字符串是String类型的,不支持占位符,比如
def name = 'Test Groovy!'
def body = 'Test $name' assert name == 'Test Groovy!'
assert body == 'Test $name' //不会替换$name站位符 字符串可以通过 + 直接拼接。
  • 三重单引号字符串是String类型的,不支持占位符插值操作,可以标识多行字符串,比如:
def aMultilineString = '''line one
line two
line three'''
  • 双引号字符串支持占位插值操作,如果双引号字符串不包含占位符则是String类型的,如果双引号字符中包含占位符则是GString类型的。对于插值占位符我们可以使用${}或者$表示,${}用于一般替代字符串或者表达式,$主要用于A.B的形式中,比如:
def name = 'Guillaume' // a plain string
def greeting = "Hello ${name}"
assert greeting.toString() == 'Hello Guillaume' def sum = "The sum of 2 and 3 equals ${2 + 3}"
assert sum.toString() == 'The sum of 2 and 3 equals 5' def person = [name: 'Guillaume', age: 36]
assert "$person.name is $person.age years old" == 'Guillaume is 36 years old' //注意的是:$只对A.B等有效,如果表达式包含括号、大括号、闭包等符号则是无效的。比如: def number = 3.14 shouldFail(MissingPropertyException) { println "$number.toString()" } //该代码运行抛出groovy.lang.MissingPropertyException异常,因为Groovy认为去寻找number的名为toString的属性,所以异常
  • 还有一个很NB的结论,一个普通插值表达式替换实际是在GString创建的时刻,一个包含闭包的表达式由于延迟运算调用toString()方法,所以会产生一个新的字符串值。
def number = 1
def eagerGString = "value == ${number}"
def lazyGString = "value == ${ -> number }" assert eagerGString == "value == 1"
assert lazyGString == "value == 1"

// 当number = 2 时,两者出现了不同
number = 2
assert eagerGString == "value == 1"
assert lazyGString == "value == 2"
  • GString和String的hashCode是不一样的。
  • 多重双引号字符串也支持占位插值操作。
  • 斜线字符串和双引号字符串类似,通常用在正则表达式中。
//普通使用
def fooPattern = /.*foo.*/
assert fooPattern == '.*foo.*'
//含转义字符使用
def escapeSlash = /The character \/ is a forward slash/
assert escapeSlash == 'The character / is a forward slash'
//多行支持
def multilineSlashy = /one
two
three/ assert multilineSlashy.contains('\n')
//含站位符使用支持
def color = 'blue'
def interpolatedSlashy = /a ${color} car/ assert interpolatedSlashy == 'a blue car'
  • Groovy没有明确的Characters,但是我们可以用如下三种方式处理字符
char c1 = 'A'
assert c1 instanceof Character def c2 = 'B' as char
assert c2 instanceof Character def c3 = (char)'C'
assert c3 instanceof Character

5)Lists类型

  Groovy支持List类型,可以增加或者删除对象,列表中的对象不受类型限制,比较特殊的是可以通过超出列表范围的数字来索引列表。可以做个小例子:

//和java中的思想是一样的
def numbers=[1,2,3];
assert numbers instanceof List
assert numbers.size() == 3 //存储任意类型
def test = [1, 'a', true] //使用as强制类型转换
def linkedList = [2,3,4] as LinkedList
assert linkedList instanceof java.util.LinkedList //给List追加item
def letters = ['a', 'b','c','c']
letters << 'e'
assert letters[4] == 'e' //多维List支持
def multi = [[0, 1], [2, 3]]
assert multi[1][0] == 2

6)Map类型

Map是“键-值”对的集合,在Groovy中键key不一定是String,可以是任意对象。

def colors=[red:'1',green:'2', blue:'3']
assert colors.red == '1' //对于map需要特别注意一种情况,如下:
//把一个定义的变量作为Map的key的正确写法---添加括弧,访问Map的该key是成功的
def key = 'name'
def person=[(key):'liwei']
assert !person.containsKey('key')
assert person.containsKey('name')

7)Range类

  Range是Groovy对List的一种拓展:

def aRange = 1..5  <==Range类型的变量 由begin值+两个点+end值表示
左边这个aRange包含1,2,3,4,5这5个值 如果不想包含最后一个元素,则 def aRangeWithoutEnd = 1..<5 <==包含1,2,3,4这4个元素
println aRange.from
println aRange.to

2 闭包


1)闭包(Closure)的定义

  闭包是Groovy中非常重要的一个数据类型或者说一个概念。它代表了一段可执行的代码,其外形如下:

def aClosure = {//闭包是一段代码,所以需要用花括号括起来..
String param1, int param2 -> //这个箭头很关键。箭头前面是参数定义,箭头后面是代码
println"this is code" //这是代码,最后一句是返回值,
//也可以使用return,和Groovy中普通函数一样
} //简而言之,闭包的定义格式是:
def xxx={paramters ——> code}
def xxx={无参数,纯code} 这种case不需要 ——>符号 //调用闭包的方式:
xxx.call(paramter1,paramter2...)
xxx(paramter1,paramter2....) //如果闭包没有定义参数的话,则隐含有一个参数,这个参数交 it, 和java中this类似。
def xxx = {“Hello,$it!”}
assert xxx('Groovy') == 'Hello Groovy!' //不过如果想定义的本来就是没有参数,则可以定义如下
def noParamClosure = {—> true}

2)使用中注意的点

  • 省略圆括号
//闭包在Groovy中大量使用,比如下面这个函数,最后一个参数是闭包
public static <T> List<T> each(List<T> self, Closure closure) //上面这个方法是List中的,表示可以对List中每一个元素在闭包中做一些处理,我们看下如何使用它:
def iamList = [1,2,3,4,5]  //定义一个List
iamList.each{ //调用它的each,这段代码的格式看不懂了吧?each是个函数,圆括号去哪了?
println it
}
//这里有有两个知识点:
1)Groovy中,当函数的最后一个参数是闭包的话,可以省略圆括号。
2)如何确定Closure参数   我们还以上面的那个iamList为例,通过查看API可以知道 【递归set中的元素,传递每一个item给闭包】
所以参数其实就明确了,是List中的每一个元素。
  同理我们也可以得出,对于Map来说,它的类似方法中的闭包可以有两个参数,以findAll为例,会将
key和Value分别传进去。 从这里我们可以看出,一定要对API熟悉,才能很好的使用闭包。 //我们在平时的gradle项目中也常常见到
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}

3 脚本类、文件IO和XML操作


1)脚本类

  • 脚本中import其他类

   在Groovy中,系统自带会加载当前目录及其子目录下的xxx.groovy文件。

  • 脚本到底是什么

   当通过 groovy xxx.groovy运行脚本时,Groovy会先把xxx.groovy中的内容转换成一个java类(groovyc -d classes test.groovy)。这个java类有以下特点: 

 1. xxx.groovy被转换成一个test类,继承script
2. 每一个脚本都会生成一个static main 函数,所以当执行 groovy test.groovy时,其实就是执行了main方法
3. 脚本中所有代码都会放到run方法中。
4. 如果脚本中定义了函数,则函数会被定义在test类中。
  • 脚本中的变量和作用域 
def x = 1 <==注意,这个x有def(或者指明类型,比如 int x = 1)
def printx(){
println x //这里会报错,提示x找不到
} //其实从上面的介绍已经可以知道为啥这里为啥报错了。
原因就是:printx方法在类中是一个成员函数,但是x会被放到 run方法中,所以printx方法是不可能访问到x元素的。
 

2)文件I/O操作

  • 读文件
def targetFile=new File(文件名) //创建File对象

targetFile.eachLine{   //读文件中的每一行
  String oneLine ->
  println oneLine
} targetFile.getBytes() //文件一次性读出,返回类型为byte【】 def ism = targetFile.newInputStream() //流
ism.close targetFile.withInputStream{
  ism ->
不需要close, Groovy会自动替你close
}
  • 写文件
def srcFile = new File(源文件名)
def targetFile = new File(目标文件名)
targetFile.withOutputStream{ os->
srcFile.withInputStream{ ins->
os << ins //利用OutputStream的<<操作符重载,完成从inputstream到OutputStream
//的输出
}
}

3)XML操作

  Groovy提供了一个GPath。直接上例子看下怎么用

<!--test.xml-->
<response version-api="2.0">
<value>
<books>
<book available="20" id="1">
<title>Don Xijote</title>
<author id="1">Manuel De Cervantes</author>
</book>
<book available="14" id="2">
<title>Catcher in the Rye</title>
<author id="2">JD Salinger</author>
</book>
<book available="13" id="3">
<title>Alice in Wonderland</title>
<author id="3">Lewis Carroll</author>
</book>
<book available="5" id="4">
<title>Don Xijote</title>
<author id="4">Manuel De Cervantes</author>
</book>
</books>
</value>
</response>
//第一步,创建XmlSlurper类
def xparser = new XmlSlurper()
def targetFile = new File("test.xml")
//轰轰的GPath出场
GPathResult gpathResult =xparser.parse(targetFile) //开始玩test.xml。现在我要访问id=4的book元素。
//下面这种搞法,gpathResult代表根元素response。通过e1.e2.e3这种
//格式就能访问到各级子元素....
def book4 = gpathResult.value.books.book[3]
//得到book4的author元素
def author = book4.author
//再来获取元素的属性和textvalue
assert author.text() == ' Manuel De Cervantes '
获取属性更直观
author.@id == '4' 或者 author['@id'] == '4'
属性一般是字符串,可通过toInteger转换成整数
author.@id.toInteger() == 4
好了。GPath就说到这。再看个例子。我在使用Gradle的时候有个需求,就是获取AndroidManifest.xml版本号(versionName)。有了GPath,一行代码搞定,请看:
def androidManifest = newXmlSlurper().parse("AndroidManifest.xml")
println androidManifest['@android:versionName']
或者
println androidManifest.@'android:versionName'

4 总结


  Groovy是一门语言,因此一篇文章不可能表达的很清楚,只是作为入门教程,对Groovy有个大概的认识,为之后学习Gradle打基础,如果想更深入的了解Groovy,需要自行的实践和Google了。

参考博客:

  http://www.infoq.com/cn/articles/android-in-depth-gradle/

api文档

  http://tool.oschina.net/apidocs/apidoc?api=groovy

【gradle使用前篇—Groovy简介】的更多相关文章

  1. iOS开发UI篇—CALayer简介

    iOS开发UI篇—CALayer简介   一.简单介绍 在iOS中,你能看得见摸得着的东西基本上都是UIView,比如一个按钮.一个文本标签.一个文本输入框.一个图标等等,这些都是UIView. 其实 ...

  2. VS2010+MVC4+Spring.NET2+NHibernate4-传统三层架构-前篇

    VS2010+MVC4+Spring.NET2+NHibernate4 - 传统三层架构 - 前篇 一直追求使用开源项目,就因一个字:懒! 一直想整理一下的,却一直懒到现在!从当初用的MVC3到现在的 ...

  3. 【翻译】西川善司「实验做出的游戏图形」「GUILTY GEAR Xrd -SIGN-」中实现的「纯卡通动画的实时3D图形」的秘密,前篇(2)

    Lighting和Shading(2)镜面反射的控制和模拟次级表面散射技术 http://www.4gamer.net/games/216/G021678/20140703095/index_2.ht ...

  4. iOS开发多线程篇—多线程简介

    iOS开发多线程篇-多线程简介 一.进程和线程 1.什么是进程 进程是指在系统中正在执行的一个应用程序 每一个进程之间是独立的.每一个进程均执行在其专用且受保护的内存空间内 比方同一时候打开QQ.Xc ...

  5. ASP.NET自定义控件组件开发 第三章 为控件添加事件 前篇

    原文:ASP.NET自定义控件组件开发 第三章 为控件添加事件 前篇 第三章 为控件添加事件 好了,我们之前以前开发一个控件.而且也添加了属性,开发也很规范,但是那个控件还差最后一点:添加事件. 系列 ...

  6. 【原创】构建高性能ASP.NET站点 第六章—性能瓶颈诊断与初步调优(下前篇)—简单的优化措施

    原文:[原创]构建高性能ASP.NET站点 第六章-性能瓶颈诊断与初步调优(下前篇)-简单的优化措施 构建高性能ASP.NET站点 第六章—性能瓶颈诊断与初步调优(下前篇)—简单的优化措施 前言:本篇 ...

  7. [原创].NET 分布式架构开发实战之四 构建从理想和实现之间的桥梁(前篇)

    原文:[原创].NET 分布式架构开发实战之四 构建从理想和实现之间的桥梁(前篇) .NET 分布式架构开发实战之四 构建从理想和实现之间的桥梁(前篇) 前言:上一篇文章讲述了一些实现DAL的理论,本 ...

  8. webpack2 前篇

    webpack2 前篇 #webpack 前两天用了一天半时间琢磨了下webpack2,想起去年这时候,面对webpack1那样恶心的文档,前前后后搞了好几次才摸索清楚,那真是吐了. 划重点 其实we ...

  9. 文本分类需要CNN?No!fastText完美解决你的需求(前篇)

    http://blog.csdn.net/weixin_36604953/article/details/78195462?locationNum=8&fps=1 文本分类需要CNN?No!f ...

随机推荐

  1. ef core实现无感知软删除

    很多web程序一般的偶不会设计真的物理删除了. 基本上都是在在数据库加一个标记,就得当作已经删除了.同时在查询的时候,过滤已经标记删除的数据 ef core实现软删除是非常简单的,直接在OnModel ...

  2. 【TencentOS tiny】深度源码分析(8)——软件定时器

    软件定时器的基本概念 TencentOS tiny 的软件定时器是由操作系统提供的一类系统接口,它构建在硬件定时器基础之上,使系统能够提供不受硬件定时器资源限制的定时器服务,本质上软件定时器的使用相当 ...

  3. SpringCloud教程一:eureka注册中心(Finchley版)

    一.spring cloud简介 本阶段学习教程Spring Boot版本2.0.3.RELEASE,Spring Cloud版本为Finchley.RELEASE. Finchley版本的官方文档如 ...

  4. 记录JS如何使用广度遍历找到节点的所有父节点

    我们在实际的工作业务场景中经常遇到这样的场景,求取树数据中某个节点的父亲节点以及所有的父亲节点,这样的场景下不建议使用深度遍历,使用广度遍历可以更快找到. 1.案例解说 比如树的长相是这样的: 树的数 ...

  5. .NET Core API后台架构搭建

    ASP.NET Core API后台架构搭建 项目文件:https://files.cnblogs.com/files/ZM191018/WebAPI.zip 本篇可以了解到: 依赖注入 Dapper ...

  6. web安全之php中常见的INI文件配置

    php.ini 在 PHP 启动时被读取.对于服务器模块版本的 PHP,仅在 web 服务器启动时读取 一次.对于 CGI 和 CLI 版本,每次调用都会读取. * Apache web 服务器在启动 ...

  7. How to Compute The Derivatives (如何求导数)(TBC)

    A video by 3Blue1Brown in Bilibili\text{A video by 3Blue1Brown in Bilibili}A video by 3Blue1Brown in ...

  8. 为什么阿里巴巴Java开发手册中不建议在循环体中使用+进行字符串拼接?

    之前在阅读<阿里巴巴Java开发手册>时,发现有一条是关于循环体中字符串拼接的建议,具体内容如下: 那么我们首先来用例子来看看在循环体中用 + 或者用 StringBuilder 进行字符 ...

  9. Python斐波那契数列

    今天偶然看到这个题目,闲着没事练一下手 if __name__ == '__main__': """ 斐波那契数列(Fibonacci sequence), 又称黄金分割 ...

  10. 【教程】基于Ubuntu系统的PyTorch虚拟环境配置

    目录 一.PyTorch虚拟环境配置 二.PyTorch虚拟环境使用 三.常用命令 Editor: Veagau Time: 2019/10/17 一.PyTorch虚拟环境配置 该部分操作均在终端( ...