groovy 基础语法
一, 变量
1, 类型
对象类型, 基本类型(本质是对象类型)
- int x = 0
- println x.class
- double y = 3.14
- println y.class
输出:
- class java.lang.Integer
- class java.lang.Double
结论: 从 log 输出可以得出, groovy 中实质上是没有基本类型的, 本质是 java 中的装箱对象类型.
2, 定义
强类型定义方式, 弱类型 def 定义方式
- def x_1 = 11
- println x_1.class
- def y_1 = 3.1415
- println y_1.class
- def name = 'Android'
- println name.class
输出:
- class java.lang.Integer
- class java.math.BigDecimal
- class java.lang.String
二, 字符串
String,GString
1, 常用的三种定义方式
- // 单引号: 与 java 中的 String 是一样的
- def name = 'a single \'a\'string'
- println name.class
- // 三引号: 可以直接指定格式(如: 换行, 不需要加 \ n, 代码表示上也不需要用 + 号拼接)
- def thupleName = '''three single string'''
- println thupleName.class
- // 三引号: 需要每行文字声明不同行(即: 三引号后不要紧接文字内容), 可以用 \.
- def thupleName1 = '''\
- line one
- line two
- line three'''
- println thupleName1
- // 双引号: 可扩展字符串(即: 可包含变量, 表达式等)
- def doubleName = "this a common String" // String
- println doubleName.class
- def doubleName1 = "Hello: ${name}" // GString
- println doubleName1.class
- // 单引号
- class java.lang.String
- // 三引号
- class java.lang.String
- line one
- line two
- line three
- // 双引号
- class java.lang.String
- class org.codehaus.groovy.runtime.GStringImpl
2, 新增操作符
- def str = "groovy"
- def str2 = "gro"
- // center(numberOfChars, padding): 使用 padding 对已有字符串两边进行填充
- // center(numberOfChars): 使用空格对已有字符串两边进行填充
- println str.center(8,'a')
- // padLeft(numberOfChars, padding): 使用 padding 对已有字符串左边进行填充
- println str.padLeft(8,'a')
- // 比较: 可以使用>,<符号来直接比较, 而不需要使用 compareTo()
- println str> str2
- // 获取字符: string[index](相当于 string.getAt(index))
- println str[0]
- println str[0..1]
- // 减去字符串: 可以直接使用 - 号, 效果与 minus()一致
- println str - str2 // str.minus(str2)
- // 倒序
- println str.reverse()
- // 首字母大写
- println str.capitalize()
- // 判断是否是数字
- println str.isNumber()
输出:
- // center
- agroovya
- // padLef
- aagroovy
- // str> str2
- true
- // str[0]
- g
- // str[0..1]
- gr
- // str - str2
- ovy
- // reverse
- yvoorg
- // capitalize
- Groovy
- // isNumber
- false
三, 新增 API 讲解
逻辑控制: 顺序逻辑(单步往下执行), 条件逻辑(if/else,switch/case), 循环逻辑(while,for)
- // ============= 条件逻辑 =============
- def x = 1.23
- def result
- switch(x){
- case 'foo':
- result = 'found foo'
- break
- case [4,5,6,'inlist']: // 列表
- result = 'list'
- break
- case 12..30: // 范围
- result = "range"
- break
- case Integer:
- result = 'integer'
- break
- case BigDecimal:
- result = 'big decimal'
- break
- default:
- result = 'default'
- break
- }
- println result
- // ============= 循环逻辑 =============
- def sum = 0
- // 对范围的 for 循环
- for(i in 0..9){sum += i}
- // 对 List 的循环
- for(i in [0,1,2,3,4,5,6,7,8,9]){sum += i}
- // 对 Map 的循环
- for(i in ["lili":1, "luck":2, "xiaoming":3]){
- sum += i.value
- }
输出:
- // x = 1.23
- big decimal
- // x = 4
- list
groovy 闭包讲解
一, 基础
闭包概念(定义, 调用), 闭包参数(普通参数, 隐式参数), 闭包返回值(总是有返回值的)
- // ============= 闭包概念 =============
- // 定义
- def clouser = {
- println 'Hello groovy!'
- }
- // 调用 1:
- clouser.call()
- // 调用 2:
- clouser()
- // ============= 闭包参数 =============
- // 普通参数
- def clouser = {
- String name, int age -> println "Hello ${name}, my age is ${age}"
- }
- clouser('groovy!', 18) // clouser.call('groovy!', 18)
- // 隐式参数(it 是所有闭包都拥有的默认参数, 当有显式声明参数时, it 失效)
- def clouser = {
- println "Hello ${it}"
- }
- clouser('groovy!')
- // ============= 闭包返回值 =============
- def clouser = {
- return 'Hello, groovy!'
- } // 返回 Hello, groovy
- def result = clouser()
- println result
- def clouser = {
- println 'Hello groovy!'
- } // 返回 null
- def result = clouser()
- println result
二, 使用
闭包的用法: 与基本类型的结合使用, 与 String 结合使用, 与数据结构结合使用, 与文件等结合使用
- // ============= 与基本类型的结合使用 =============
- int x = fab(5)
- println x
- // 用来求指定 number 的阶乘
- int fab(int number){
- int result = 1
- 1.upto(number, {num -> result *= num}) // upto()是 DefaultGroovyMethods 中封装好的方式
- return result
- }
- int fab2(int number){
- int result = 1
- number.downto(1){ // 闭包可以不写在括号内, 可以写在括号外
- num -> result *= num
- }
- return result
- }
- // 从 0 循环到 number
- int cal(int number){
- int result = 1
- number.times { // times(Closure)只接收一个闭包, 把闭包写到括号外, 括号可以省略
- num -> result += num
- }
- return result
- }
- // ============= 与 String 结合使用包参数 =============
- String str = 'the 2 and 3 is 5'
- // each: 遍历每个字符
- str.each {
- String temp -> print temp
- }
- // find: 查找符合条件的第一个
- println str.find {
- String s -> s.isNumber()
- }
- // findAll: 查找符合条件的所有字符
- def list = str.findAll {
- String s -> s.isNumber()
- }
- println list.toListString()
- // any: 遍历每个字符, 只要满足条件就返回 true
- def result= str.any {
- String s -> s.isNumber()
- }
- println result
- // every: 遍历每个字符, 所有都要满足条件才会返回 true
- println str.every {
- String s -> s.isNumber
- }
- // collect: 遍历每个字符, 经过闭包处理后, 添加进 list 中返回
- def list = str.collect { it.toUpperCase() }
- println list.toListString()
- // ============= 与数据结构结合使用 =============
- // ============= 与文件等结合使用 =============
- // 后续篇章中会涉及
输出:
- // ============= 与 String 结合使用包参数 =============
- // each
- the 2 and 3 is 5
- // find
- 2
- // final All
- [2, 3, 5]
- // any
- true
- // every
- false
- // collect
- [T, H, E, , 2, , A, N, D, , 3, , I, S, , 5]
三, 进阶
闭包关键变量(this,owner,delegate)
- // this == owner == delegate
- def scriptClosure = {
- println "scriptClosure this:"+this // 代表闭包定义处的类
- println "scriptClosure owner:"+owner // 代表闭包定义处的类或者对象
- println "scriptClosure delegate:"+delegate // 代码任意对象, 默认与 owner 一致
- }
- scriptClosure.call()
- // this != owner == delegate
- def nestClosure = {
- def innerClosure = {
- prinln "innerClosure this:"+this
- prinln "innerClosure owner:"+owner
- prinln "innerClosure delegate:"+delegate
- }
- innerClosure.call()
- }
- // this != owner != delegate
- def nestClosure = {
- def innerClosure = {
- prinln "innerClosure this:"+this
- prinln "innerClosure owner:"+owner
- prinln "innerClosure delegate:"+delegate
- }
- innerClosure.delegate = new Person() // 手动修改 delegate 的值
- innerClosure.call()
- }
输出
- // this == owner == delegate
- scriptClosure this:variable.closurestudy@2ef3eef9
- scriptClosure owner:variable.closurestudy@2ef3eef9
- scriptClosure delegate:variable.closurestudy@2ef3eef9
- // this != owner == delegate
- innerClosure this:variable.closurestudy@2ef3eef9
- innerClosure owner:variable.closurestudy$_run_closure2@402bba4f
- innerClosure delegate:variable.closurestudy$_run_closure2@402bba4f
- // this != owner != delegate
- innerClosure this:variable.closurestudy@2ef3eef9
- innerClosure owner:variable.closurestudy$_run_closure2@402bba4f
- innerClosure delegate:variable.Person@795cd85e
结论:
在大多数据情况下, this,owner,delegate 的值是一样的.
在闭包中定义闭包时, this 与 owner 的值是不一样的.(this 指的是闭包定义处的类对象, owner 指的是闭包定义处类中的闭包对象)
在手动修改了闭包 delegate 时, owner 与 delegate 的值才会不一样.
闭包委托策略(Closure.OWNER_FIRST,Closure.OWNER_ONLY,Closure.DELEGATE_FIRST,Closure.DELEGATE_ONLY)
- class Studen{
- String name
- def pretty = { "My name is ${name}" }
- String toString(){
- pretty.call()
- }
- }
- Class Teacher{
- String name1 // 注意: 与 Student 中的 name 变量名不一样!!
- }
- def stu = new Studen(name: "Lqr")
- def tea = new Teacher(name1: "Lxf")
- // 1,Normal
- println stu.toString()
- // 2, 优先委托 delegate
- stu.pertty.delegate = tea
- stu.pertty.resolveStrategy = Closure.DELEGATE_FIRST
- println stu.toString()
- // 3, 只委托 delegate
- stu.pertty.delegate = tea
- stu.pertty.resolveStrategy = Closure.DELEGATE_ONLY
- println stu.toString()
输出:
- // 1,Normal
- My name is Lqr
- // 2, 优先委托 delegate
- My name is Lqr // 如果 Teacher 中的 name1 改为 name, 则输出变为: My name is Lxf
- // 3, 只委托 delegate
报错: No Such property: name for class Teacher
groovy 数据结构
一, 列表
定义, 操作(增删查排)
- // 列表的定义
- def list = [1, 2, 3, 4, 5] // groovy 中的列表就是 ArrayList
- println list.class
- println list.size()
- // 数组的定义
- def array = [1, 2, 3, 4, 5] as int[] // 使用 as int[]转换
- int[] array = [1, 2, 3, 4, 5] // 使用强类型定义
- // ============= 列表的添加 =============
- list.add(6)
- list.leftShift(7)
- list <<8
- println list.toListString()
- def plusList = list + 9
- println plusList.toListString()
- // ============= 列表的删除 =============
- list.remove(7)
- list.remove((Object)7)
- list.removeAt(7)
- list.removeElement(6)
- list.removeAll { return it % 2 == 0}
- println list - [6, 7]
- println list.toListString()
- // ============= 列表的排序 =============
- def sortList = [6 -3, 9, 2, -7, 1, 5]
- sortList.sort() // java 中: Collections.sort(sortList)
- println sortList
- // 自定义排序规则: 按绝对值反方向排序
- sortList.sort { a,b ->
- a == b ? 0 : Math.abs(a) <Math.abs(b) ? 1 : -1
- }
- // 自定义排序规则: 按字符串长度排序
- def sortStringList = ['abc', 'z', 'Hello', 'groovy', 'java']
- sortStringList.sort { it -> return it.size()}
- println sortStringList
- // ============= 列表的查找 =============
- def findList = [-3, 9, 6, 2, -7, 1, 5]
- // find() : 返回第一个符合条件的元素
- int result = findList.find { return it % 2 == 0}
- println result
- // findAll() : 返回所有符合条件的元素
- def resultfindList.findAll{ return it % 2 != 0}
- println result.toListString()
- // any() : 若列表中有一个满足条件就返回 true
- def result = findList.any { return it % 2 != 0}
- println result
- // every() : 列表中所有元素都满足条件才返回 true
- def result = findList.every {return it % 2 != 0}
- println result
- // min() : 返回最小值
- println findList.min() // findList.min { return Math.abs(it) } 查找最小绝对值
- // max() : 返回最大值
- println findList.max() // findList.max { return Math.abs(it) } 查找最大绝对值
- // count() : 统计符合条件的元素个数
- def num = findList.count { return it % 2 == 0}
- println num
输出
- // 列表的定义
- class java.util.ArrayList
- 5
- // ============= 列表的排序 =============
- [-7, -3, 1, 2, 5, 6, 9]
- // 自定义排序规则: 按绝对值反方向排序
- [9, -7, 6, 5, -3, 2, 1]
- // 自定义排序规则: 按字符串长度排序
- ['z', 'abc', 'java', 'Hello', 'groovy']
- // ============= 列表的查找 =============
- // find
- 6
- // findAll()
- [-3, 9, -7, 1, 5]
- // any()
- true
- // every()
- false
- // min()
- 1
- // max()
- 9
- // count()
- 2
二, 映射
- // 定义
- def colors = [
- red : 'ff0000',
- green : '00ff00',
- blue : '0000ff'
- ]
- println colors.getClass() // 注: 不能直接使用 colors.class, 因为这样会查找 key 为 class 的元素
- // 强行指定类型: def colors = [...] as HashMap 或 HashMap colors = [...]
- // 索引方式
- println colors['red']
- println colors.red
- println colors.blue
- // 添加元素
- colors.yellow = 'ffff00' // 添加同类型 key-value
- colors.complex = [a:1, b:2] // 添加任意类型 key-value
- println colors.toMapString()
- // 删除元素
- colors.remove(red)
- def students = [
- 1: [number: '0001', name: 'Bob', score: 55, sex: 'male'],
- 2: [number: '0002', name: 'Johnny', score: 62, sex: 'female']
- 3: [number: '0003', name: 'Claire', score: 73, sex: 'female']
- 4: [number: '0004', name: 'Amy' ,score: 66, sex: 'male']
- ]
- // 遍历
- students.each { def student ->
- println "the key is ${student.key},"+
- "the value is ${student.value}"
- }
- // 带索引遍历
- students.eachWithIndex { def student, int index ->
- println "the index is ${index},"+
- "the key is ${student.key},"+
- "the value is ${student.value}"
- }
- // 直接遍历 key-value
- students.each { key, value -> ...}
- students.eachWithIndex { key, value, index -> ...}
- // 查找
- def entry = students.find { def student ->
- return student.value.score>= 60
- }
- println entry
- def entrys = students.findAll { def student ->
- return student.value.score>= 60
- }
- println entrys
- // 统计及格男生个数
- def count = students.count { def student ->
- return student.value.score>= 60 && student.value.sex == 'male'
- }
- println count
- // 过滤: 获取所有及格同学的姓名
- def names = students.findAll { def student ->
- return student.value.score>= 60
- }.collect{ // 过滤出元素指定属性列
- return it.value.name
- }
- println names.toListString()
- // 分组: 对及格与不及格学生进行分组
- def group = students.groupBy {def student ->
- return student.value.score>= 60 ? '及格' : '不及格'
- }
- println group.toMapString()
- // 排序
- def sort = students.sore { def student1, def students ->
- Number score1 = student1.value.score
- Number score2 = student2.value.score
- return score1 == score2 ? 0 : score1 <score2 ? -1 : 1
- }
- println sort
- // 定义
- class java.util.LinkedHashMap
- // 索引方式
- ff0000
- ff0000
- 0000ff
- // 添加元素
- [red:ff0000, green:00ff00, blue:0000ff, yellow:ffff00, complex:[a:1, b:2]]
- // 遍历
- the key is 1, thie value is [number:0001, name:Bob, score:55, sex:male]
- the key is 2, thie value is [number:0002, name:Johnny, score:62, sex:female]
- the key is 3, thie value is [number:0003, name:Claire, score:73, sex:female]
- the key is 4, thie value is [number:0004, name:Amy, score:66, sex:male]
- // 带索引遍历
- the index is 0, the key is 1, thie value is [number:0001, name:Bob, score:55, sex:male]
- the index is 1, the key is 2, thie value is [number:0002, name:Jhonny, score:62, sex:female]
- the index is 2, the key is 3, thie value is [number:0003, name:Claire, score:73, sex:female]
- the index is 3, the key is 4, thie value is [number:0004, name:Amy, score:66, sex:male]
- // 查找
- // find
- 2={
- number=0002, name=Johnny, score=62, sex=female
- }
- // findAll
- [2:[number:0002, name:Johnny, score:62, sex:female], 3:[number:0003, name:Claire, score:73, sex:female], 4:[number:0004, name:Amy, score:66, sex:male]]]
- // count
- 1
- // collect
- [Johnny, Claire, Amy]
- // groupBy
[不及格:[1:[number:0001, name:Bob, score:55, sex:male]], 及格:[2:[number:0002, name:Johnny, score:62, sex:female], 3:[number:0003, name:Claire, score:73, sex:female], 4:[number:0004, name:Amy, score:66, sex:male]]]]
- // 排序
- [1:[number:0001, name:Bob, score:55, sex:male], 2:[number:0002, name:Johnny, score:62, sex:female], 4:[number:0004, name:Amy, score:66, sex:male]], 3:[number:0003, name:Claire, score:73, sex:female]]
其他:
map 定义时, key 通常是用不可变字符串或 number 来定义.
字符串不使用单引号时, groovy 默认会认为是不可变的单引号字符串.(如: red 与'red' 一样)
三, 范围
Range 定义, 操作(each,switch-case)
- def range = 1..10 // Range 是一个继承于 List 的接口, 即, 本质是列表
- println range[0] // 范围第一个数值
- println range.contains(10) // 范围是否包含 10
- println range.from // 范围中第一个值
- println range.to // 范围中最后一个值
- // 遍历
- range.each {
- println it
- }
- for(i in range){
- println i
- }
- // switch-case
- def getGrade(Number number){
- def result
- switch(number){
- case 0..<60: // [0, 60)
- result = '不及格'
- break;
- case 60..<70:
- result = '及格'
- break;
- case 70..<80:
- result = '良好'
- break;
- case 80..100: // [80, 100]
- result = '优秀'
- break;
- }
- result // 相当于 return result. 此处可以省略 return,groovy 中方法会默认返回最后一行的结果
- }
groovy 面向对象
一, 类, 接口等的定义和使用
- // 类的定义
- class Person{
- String name
- Integer age // int 与 Integer 是一样的, groovy 中, int 本质就是 Integer
- def increaseAge(Integer years){
- this.name += years
- }
- }
- // 创建对象
- def person = new Person(name: 'Lqr', age: 18) // 也可以只直接一部分, 如: new Person(name: 'lqr')或不指定
- println "the name is ${person.name}, the age is ${person.age}"
- person.increaseAge(10)
- // 接口定义
- interface Action {
- void eat()
- void drink()
- void play()
- }
- // trait 类定义(与 java 中的抽象类差不多)
- trait DefaultAction {
- abstract void eat()
- void play(){
- println 'i can play.'
- }
- }
结论
groovy 中默认都是 public(类, 成员属性, 方法等)
groovy 类继承自 groovy.lang.GroovyObject(而 java 类则继承自 Object)
def 定义的方法, 其返回值就是 Object
groovy 类会默认会成员变量生成 getter 与 setter 方法.
无论你是直接. 还是调用 get/set, 最终都是调用 get/set 方法, 如: person.name 相当于 person.getName()
groovy 接口中不允许定义非 public 的方法 (如: protected void eat() 是不行的)
二, 元编程
groovy 运行时类方法调用流程:
在 java 中, 如果对象调用了一个类中没有定义过的方法时, 连编译都编译不过, 但是在 groovy 中, 情况则不同 (可以编译通过), 根据图中流程可以知道, 运行期间, 当对象调用了一个类中没有的方法时, 会依次调用 metaClass 中的同名方法, 类中的 methodMissing(String name, Object args) 方法, 类中的 invokeMethod(String name, Object args)方法, 执行到其中一个便停止后续方法查找调用.
1, 定义 invokeMethod
当只定义类的 invokeMethod(String name, Object args)方法时, 运行时调用对象一个不存在的方法时, 会执行 invokeMethod().
- class Person{
- ...
- // 一个方法找不到时, 调用它代替
- def invokeMethod(String name, Object args){
- return "the method is ${name}, the params is ${args}"
- }
- }
- def person = new Person(name: 'Lqr', age: 18)
- person.cry()
输出:
the method is cry, the params is []
2, 定义 methodMissing
当同时定义了类中的 invokeMethod(),methodMissing()方法时, 优先执行 methodMissing(String name, Object args)
- class Person{
- ...
- // 一个方法找不到时, 调用它代替
- def invokeMethod(String name, Object args){
- return "the method is ${name}, the params is ${args}"
- }
- def methodMissing(String name, Object args){
- return "the method ${name} is missing"
- }
- }
- def person = new Person(name: 'Lqr', age: 18)
- person.cry()
输出:
the method cry is missing
3,metaClass
metaClass 便是 groovy 中的元编程核心. 在 groovy 中, 可以使用 metaClass 为类在运行时动态添加 属性 和 方法
用处: 对第三方库中的 final 类进行扩展.
- // 为类动态添加一个属性
- Person.metaClass.sex = 'male' // 同时设置动态属性 sex 的默认值为 male
- def Person = new Person(name: 'Lqr', age: 18)
- println person.sex
- person.sex = 'female'
- println "the new sex is:" + person.sex
- // 为类动态添加方法
- Person.metaClass.sexUpperCase = { -> sex.toUpperCase() }
- def person2 = new Person(name: 'Lqr', age: 18)
- println person2.sexUpperCase()
- // 为类动态添加静态方法
- Person.metaClass.static.createPerson = {
- String name, int age -> new Person(name: name, age: age)
- }
- def person3 = Person.createPerson('Lqr', 18)
- println person3.name + 'and' + person.age
输出:
- // 为类动态添加一个属性
- male
- the new sex is:female
- // 为类动态添加方法
- MALE
- // 为类动态添加静态方法
- Lqr and 18
以上可以看到 groovy 的 metaClass 功能十分强大, 但它本身有一个限制需要我们注意一下, 即: 默认情况下 metaClass 注入的属性与方法只是短暂的(准确来说是非全局的).
举个例子在 ClassA 中对 Person 进行了 metaClass 扩展并正常调用动态注入的属性和方法, 但是在 ClassB 中, 若也要使用前面动态注入的属性和方法是不行的, 因为在 groovy 中 metaClass 动态注入的属性和方法默认是非全局的, 你可以有如下 2 种做法:
在 ClassB 中再使用 metaClass 再动态注入一次属性和方法.
使用 ExpandoMetaClass.enableGlobally().
4,ExpandoMetaClass.enableGlobally()
使用 ExpandoMetaClass.enableGlobally()开启 metaClass 动态全局注入 属性, 方法 功能.
- // 在 ApplicationManager 调用 ExpandoMetaClass.enableGlobally(), 并对 Person 进行扩展
- class ApplicationManager{
- static void init(){
- ExpandoMetaClass.enableGlobally()
- // 为第三方类添加方法
- Person.metaClass.static.createPerson = { String name, int age ->
- new Person(name: name, age: age)
- }
- }
- }
- // 在 ClassB 中可以使用 Person 动态注入的扩展方法了
- class ClassB{
- def test(){
- def person = Person.createPerson('Lqr', 18) // 因为只是普通方法, 不是构造函数, 所以不能这样: createPerson(name: 'Lqr', age: 18) , 切记!!
- println "the person name is ${person.name} and the age is ${person.age}"
- }
- }
- // Entry 模拟一个 App 的运行入口
- class Entry{
- static void main(String[] args){
- ApplicationManager.init()
- new ClassB().test()
- }
- }
输出:
the person name is Lqr and the age is 18
来源: https://juejin.im/post/5c307717518825260a7dc27c