Groovy快速入门
领域特定语言DSL(Domain Specific Language):其核心思想为"求专不求全,解决特定问题",
Groovy: http://www.groovy-lang.org/
Groovy:是一种基于JVM的敏捷开发语言,结合了Python、Ruby等脚本语言的许多强大特性,可以与Java完美结合,使用Java的所有库;
- 语法上支持动态类型,闭包等新一代语言特性
- 无缝继承所有Java的库(可以完全用Java写,但是不推荐)
- 即支持面向对象编程,也支持面向过程编程
优势:一种更加敏捷的编程语言;入门非常容易,但是功能非常强大;既可以作为编程语言也可以作为脚本语言;熟练掌握Java的人会非常容易掌握Groovy
Groovy开发环境搭建:
java平台上有各种语言的翻版,例如kotlin对c#,scala对haskell,Clojure对lisp,groovy对ruby
macOS/Linux
- 安装配置好JDK
- 下载Groovy SDK并解压到合适未知
- 配置bin目录到/.bash_profile中,如添加如下内容到/.bash_profile
1 | export PATH=$PATH:/usr/groovy-2.5.5/bin |
- groovy -version
InteliJ IDEA配置
- 确保已安装Groovy插件
- 配置Groovy SDK未知
- 编写Groovy版本HelloWorld,注意查看编译后文件
1 | //Java版本 |
1 | //Groovy版本 |
Groovy基础语法
- 变量
- 变量的类型:基本类型+对象类型,Groovy中没有基本类型,所有的基本类型都会被编译器包装成对象类型,如:int->Integer
- 变量的定义:强类型定义方式+弱类型def定义方式,Groovy中如果变量的值可以推断除其类型,则可通过def声明为弱类型,区别于java中的强类型定义方式,如:int i=1;
- 推荐在自有使用模块使用def方式定义,如果有其他模块或其他类使用推荐强类型定义方式
- 字符串:String + GString
String的使用和Java中一致,GString定义方式如下:
- def name = ‘Hello name’ //不支持可扩展字符串
- def doubleName = “Hello doubleName” //可扩展字符串,如:def doubleName = “Hello doubleName and ${name}”,结果为: Hello doubleName and Hello name,此时doubleName是org.codehaus.groovy.runtime.GStringImpl的子类
- def thupleName = ‘’‘Hello thupleName’’’ //支持多行方式
无可扩展字符串时以上3种方式的String都是java.lang.String的子类,编码过程中String和GString是可以通用的,更多从方便使用角度考虑即可
Groovy字符串方法介绍
- java中String原有的方法
- DefalutGroovyMethods
- StringGroovyMethods:普通类型的参数+闭包类型的参数
1 | def str = "Groovy",str2 = "Hello",str3= "Hello minus" |
逻辑控制:单步顺序执行|if/else|switch-case|while|for,基本和Java操作一致,针对Groovy扩展介绍如下:
1 | //switch-case |
for循环控制
1 | //对范围的for循环 |
- 闭包
- 闭包就是一个代码块,所以需要通过{}括起来,def clouser = { println “Hello Groovy!”};clouser.call();clouser(); //推荐call()方式调用来区分是闭包
- def clouser = {String name -> println “Hello Groovy ${name}!”};clouser.call(“a”);clouser(“b”);
- 隐式参数it:def clouser = {println “Hello Groovy ${it}!”};clouser.call(“a”);clouser(“b”);
- 闭包返回值:总是有返回值的,当闭包体没有明确返回值的时候,返回结果就是null
- 如果最后一个参数是闭包,闭包可以写在外面
闭包的使用
- 与基本类型的结合使用
1 | int fab1(int number){ |
- 与String结合使用
1 | def str = "the 2 and 3 is 5" |
- 与数据结构结合使用
- 与文件等结合使用
- 闭包进阶:闭包关键字(this,owner,delegate)+闭包委托策略
1 | def scriptClosure = { |
总结:this、owner、delegate的值在大多数情况都是一样的,在嵌套闭包中this的值和owner、delegate的值不一致,只有在给delegate赋值后owner和delegate的值才不一致
闭包的委托策略
1 | class Student{ |
- 数据结构
列表的定义:def list = [1,2,3,4] 此为一个ArrayList;
数组的定义:def list = [1,2,3,4] as int[]; int[] arr=[1,2,3]
列表的排序:def list = [1,12,3,444]; Collections.sort(list);list.sort()
map的定义:def colors=[“red”:1,“blue”:2];def colors=[red:1,blue:2]
索引map:println colors.red;println colors[“red”];
添加map:colors.yellow=3 //Groovy中map可以添加不同类型的数据:colors.complex =[a:1,b:2]
遍历map: list.each{def tmp,int index -> …} list.eachWithIndex{ key,value,index -> …}
查找map: any find findAll every
分组map: groupBy{}
排序map:sort
范围Range:定义 def range = 1…10;println range[0];range.contains(10);println range.from;println range.to
Range继承自java.util.List
1 | switch(number){ |
- 面向对象
类、接口等的定义和使用
- Groovy中默认类、方法等都是public类型
- Groovy中无论是直接调用属性还是通过get/set方法其实质都是通过getter/setter方法调用的属性,getter/setter方法默认自动继承
- 接口实现需实现接口的所有方法;trait中可以有默认方法实现,没有实现的方法需添加abstract关键字,实现时只需实现abstract的方法即可
元编程(Metaprogramming)是指某类计算机程序的编写,这类计算机程序编写或者操纵其他程序(或者自身)作为它们的数据,或者在运行时完成部分本应在编译时完成的工作。很多情况下与手工编写全部代码相比工作效率更高。编写元程序的语言称之为元语言,被操作的语言称之为目标语言。一门语言同时也是自身的元语言的能力称之为反射
元编程通常有两种方式起作用。一种方式是通过应用程序接口(API)来暴露运行时引擎的内部信息。另一种方法是动态执行包含编程命令的字符串。因此,“程序能编写程序”。虽然两种方法都能用,但大多数方法主要靠其中一种。
1 | class Baby { |
结合上图理解:Java中对象方法的调用没有上图否流程分支,在Groovy中对象方法调用有否分支调用,上例中通过注释invokeMethod、methodMissing方法查看运行效果,通过以下代码可动态添加对象属性和方法:
1 | Baby.metaClass.sex = "male" |
通过ExpandoMetaClass.enableGlobally()设置让动态添加方法全局启用
- Json操作
- 对象转换成JSON字符串:JsonOutput.toJson()
- Json格式化打印:JsonOutput.prettyPrint(jsonObject)
- Json字符串转对象:def jsonSlurper = new JsonSlurper();jsonSlurper.parse()
- xml操作
- Java对xml的处理:DOM文档驱动处理方式+SAX事件驱动处理方式
- Groovy解析xml数据:def xmlSlurper = new XmlSlurper();def response = xmlSlurper.parse(xml); //response对象可以逐级访问节点,节点的属性添加@符号,如:response.books.@id,也可以通过闭包过滤信息
- Groovy深度遍历xml:可以通过逐级遍历解析后的response数据,也可通过response.depFirst().find{ …}遍历或 response.’’.find{ …}遍历(’'代表深度遍历)
- Groovy深度遍历xml:response.books.children().find{…}或response.books.’’.find{…}(’'代表深度遍历)
- Groovy创建xml数据:
1 | ''' |
- 文件操作
- Java文件处理:节点流(InputStream、OutputStream及其子类)+处理流(Reader、Writer及其子类),所有Java对文件的操作Groovy都支持
- 遍历文件内容:def file = new File(“Test.iml”);file.eachLine { println it } 或 def text = file.getText() 或 def text = file.readLines() …
gradle
Gradle是一款最新的,功能强大的构建工具,使用程序代替传统的xml配置,项目构建更加灵活,有丰富的第三方库
gradle组成:groovy核心语法+build script block+gradle api
gradle生命周期
执行./gradlew clean 观察执行过程,其生命周期包括:初始化、配置、执行
gradle生命周期的监听
在项目的build.gradle中添加如下
1 | //在配置阶段开始之前的回调 |
Gradle Project
./gradlew projects //查看工程Project数量,学会区分Project与module及根Project与子Project,每个Project必须有一个build.gradle文件
Project API组成:
- Project相关API:如何管理父Project及如何操作子Project
- this.getAllProjects(),this.getSubProjects(),this.getParent(),在gradl文件中,对应allprojects、project、subprojects进行操作
./gradlew projects可实现显示所有项目,自我实现如下:
1 | def getProjects(){ |
-
task相关API
-
属性相关API
- 父Project的属性在子项目中会被继承,可直接使用
- 可以在项目中通过common.gradle文件来定义扩展属性ext,然后在根Project中通过apply from:this.file(‘common.gradle’)引入后,按rootProject.ext.定义属性的方式使用
- 在gradle.properties中定义key-value属性,如:isLoadTest=false,然后在settings.gradle中就可以编码控制是否加载Test项目
1 | if(hasProperty('isLoadTest')? isLoadTest.toBoolean() : false){ |
自定义属性的两种方式:ext方式+gradle.properties中定义
4. file相关API
- 路径获取API:getRootdir()、getBuildDir()、getProjectDir()
- 文件操作相关API:都是基于根工程操作的,不支持跨工程操作
1 | //文件/文件夹拷贝 |
-
gradle生命周期API
-
其他API
- 依赖相关API
1 | buildscript { |
依赖传递:A模块依赖B模块,B模块依赖C模块,如果A模块也需要C模块功能,不需依赖使用,防止B修改后去掉C依赖而导致错误,可在A中引入C模块,通过exclude排除依赖,transitive禁止依赖传递
占位编译provided:A.类库只在编译阶段起作用 B.父项目已引入类库,子项目直接使用父项目类库,但为了子项目编译通过使用占位编译
- 外部命令执行
1 | task abc() { |
执行:./gradlew abc
Task
- Task定义及配置
直接通过task函数去创建
1 | task("abcd") { |
通过TaskContainer创建
1 | this.tasks.create("aa"){ |
Task创建之后可在开发工具的gradle插件重查找到task,如果没有设置group,则默认在other分组中,更多配置可通过task源码查看
2. Task执行详解
doFirst/doLast执行阶段执行,否则在配置阶段执行
1 | task aa{ |
编写一个统计build时长的task
app.gradle中编写:
1 | def startBuilderTime,endBuilderTime |
./gradlew build执行查看结果
3. Task的依赖及执行顺序
- dependsOn强依赖方式
1 | task t1() { doLast{println 'task t1'} } |
- 通过Task输入输出指定
TaskInputs:参数为任意对象及文件、文件夹;TaskOutputs:只输出文件
1 | ext{ |
destFile作为writerTask输出结果输入到readTask
- 通过API指定执行顺序:mustRunAfter/shouldRunAfter
1 | task t1() { doLast{println 'task t1'} } |
通过./gradlew t2 t1查看执行结果
4. Task类型
详见官方文档:https://docs.gradle.org/current/dsl/org.gradle.api.tasks.Delete.html#org.gradle.api.tasks.Delete
5. 构建到生命周期
1 | this.project.afterEvaluate {project -> |
Gradle其他模块
- Settings类:对应Settings.gradle
1 | if(hasProperty('isLoadTest')? isLoadTest.toBoolean() : false){ |
- SourceSet类:AndroidSourceSet/JavaSourceSet,决定了代码、资源、第三方库要存放的位置
1 | //修改.so等jnilibs的存放位置 |
- Gradle的Plugin
- Gradle没有提供创建自定义Gradle插件工程的模板,需要开发者手动创建Gradle插件工程
- 使用Groovy开发,其Gradle插件工程必须遵循如下的目录结构:
groovy代码必须位于xxxProject/src/main/groovy/目录下
提供插件属性声明文件,该文件必须位于xxxProject/src/main/resources/META-INF/gradle-plugins/xxx.properties
- android插件对gradle的扩展
android具体能配置那些属性,可查看源码通过BaseExtension查看
gradle插件: https://avatarqing.gitbooks.io/gradlepluginuserguidechineseverision/content/introduction/README.html
varints变体