Gradle技术之一 Groovy语法精讲

九路
• 阅读 1342

Gradle技术之一 Groovy语法精讲

gradle脚本是基于groovy语言开发的,想要学好gradle必须先要对groovy有一个基本的认识

1. Groovy特点

  • groovy是一种DSL语言,所谓的DSL语言,就是专门针对某一特定领域的语言,专精而不专广
  • 是一种基于JVM的开发语言,也是编译成class字节码文件
  • 结合和Python,Ruby,PHP等语言的特性,写脚本非常强大
  • Groovy可以与Java完美结合,而且可以使用Java所有的类库
  • 语法上支持动态类型,闭包等新语言的特性
  • 支持面向过程和面向对象编程

2. Groovy语法精讲

1 变量的类型和定义

  • -变量的类型分两种
    1. 基本类型:和java的基本类型一样,int,long,double,float,boolean,byte,String等 注意Groovy中,这些基本类型实际上都会被系统装箱为对应的对象类型,比如int就会被自动装箱为Integer
  1. 对象类型:和Java的一样
    • - 变量的定义
  2. 强类型定义:如 int a=10 ,指定变量的类型
  3. 弱类型定义:如 def b = 5,系统根据后面的值自动判断b是什么类型
  • - Groovy语法小点
    1. 语句后面可以不带分号,也可以带分号,如定义一个变量 int a = 10
    2. 输出一句话,直接println就行了,圆括号也可以省略,如 println "hello,world"
    3. 定义变量或者函数可以使用关键字def ,也可以按照Java的语法格式定义,如def name="tom"

如下面的代码

//强定义类型
int x = 10
println x.class

double y = 3.14
println y.class


//弱类型定义
def a = 10
println a.class

def b = 3.14
println b.class

def name = "tom"
println name.class
输出如下 :
class java.lang.Integer
class java.lang.Double
class java.lang.Integer
class java.math.BigDecimal
class java.lang.String

注意:在Groovy中,所有定义的基本类型都会被系统自动装箱为相应的包装类型

3 Groovy中的String相关

1. 字符串的定义

第一种方式:使用单引号

//使用单引号定义的字符串,里面的内容是不可以改变的
//等价于 Java中的 String name = "hello world";
def name = 'hello world'
println name 
println name.class

//输出
hello world
class java.lang.String

第二种方式:三个单引号

//之间的内容包含格式,里面的内容是什么格式的,显示出来的就是什么格式的
def content = '''
line one
line two
line three
println content'''
println content.class

//输出
line one
line two
line three
class java.lang.String

第三种方式:使用双引号

//上面两种方式定义的字符串是不可改变的,但是这种方式定义的字符是可以扩展的,里面可以包含其它的变量
def name = "android"
def str = "hello $name"
println str
println str.class

//输出
hello android
class org.codehaus.groovy.runtime.GStringImpl

注意:第三种方式定义的字符串,里面带有 $ 符号拼接的字符类型就是GString类型,其它的都还是String类型,由此可知,Groovy已经扩展了我们的字符串,可源码可知org.codehaus.groovy.runtime.GStringImpl就是继承自GString,如下:

public class GStringImpl extends GString {
    private String[] strings;
    ....

4 GString的用法

4.1 GString特性

特性1 可以拼任意表达式

def num = "3 + 5 = ${3 + 5}" //后面可以跟任意表达式
println num 
//输出
3 + 5 = 8

问:Groovy中扩展出了GString,也就是可扩展的字符串,那么Groovy中两种字符串类型,即:String,GString,使用过程中需要注意什么呢?如果一个方法中的参数是String,那么可不可以传GString 的呢? 答:没有一点关系,使用是对开发者透明的,完全不用管,可以互相使用。看下面一个函数,参数需要String,但是传的是GString,看看输出结果都是正常的,编译器也没有报错,所以这两种字符串是不用关心类型的,是可以随便用的

特性2 两种字符串类型可以互用

def num = "3 + 5 = ${3 + 5}" //后面可以跟任意表达式

//传的是GString类型的
def result = show(num)
println result


//方法接收的是String类型的
String show(String message){
    return message
}

//输出
3 + 5 = 8

//由此可以知道,GString,String在开发过程中不用刻意关心类型

4.1 String常用方法

  • 普通的方法
  • 带闭包的方法 (后面讲闭包的时候讲)
  • -普通的方法
    1. center()
    2. padLeft,padRight
    3. 可以直接比较,如下
def str = 'groovy'
def str2 = "hello"
def str3 = 'hello'
println str > str2
println str == str2
println str2 == str3

//输出
false
false
true

//也可以直接使用索引,如
println str[0]
//输出
g


//可以传入一个范围,如
println str[0..1]
//输出
gr


//减法,把str1中包含的str2减掉,如
def str1 = 'hello,world'
def str2 = 'hello'
println str1 - str2
println str1.minus(str2)
//输出
,world
,world


//字符串反转
def str3 = 'hello'
println str3.reverse()
//输出
olleh


//所有单词首字母大写
def str4 = 'hello world'
println str4.capitalize()
//输出
Hello world


//判断是否是数字类型的字符串,如
def str5 = '234'
println str5.isNumber()
//输出
true


//转化成数字,如
def str6 = '123'
println str6
//输出
123

5 Groovy中的逻辑控制

逻辑控制语句有三种

  • 顺序控制:单步往下执行
  • 条件逻辑:if/else 和 switch/case
  • 循环逻辑:while 和 for

这些逻辑控制是和Java中的一样的,但是又扩展了一些功能 其中if/elsewhile和Java中的用法是一样的,switch/casefor增加了一些扩展,我们使用下面的代码来演示这两种扩展的用法,代码如下:

//switch语句
def x = 3.14
def result
switch (x){
    case 'test':
        result = 'test'    //字符串
        break
    case [4,5,6,'test']:    //列表
        result = 'list'
        break
    case 3..11:
        result = 'range'    //范围
        break
    case Integer:
        result = 'Integer'  //类型
        break
    case BigDecimal:
        result = 'BigDecimal'
        break
    default:
        result = 'default'
        break
}

println result
//输出
BigDecimal

Groovy中的switch中可以是任何类型,数据

//1 对范围的for循环
def sum = 0
for (i in 0..3){
    sum += i
}
println sum
//输出
6


//2 对List的循环
def sum = 0
for (i in [1,2,3,4,5,6,7,8,9]){
    sum += i
}
println sum
//输出
45

//3 对Map的循环
def sum = 0
for (i in ['tom':1,'jim':2,'xiaoming':3]){
    sum += i.value
    println i.key
}
println sum
//输出
tom
jim
xiaoming
6

6 Groovy中的闭包

Groovy中的闭包很强大,下面主要从三个方向讲解闭包

  • 1 闭包的基础详解
  • 2 闭包的使用详解
  • 3 闭包的进阶详解

6.1 闭包的基础

6.1.1 闭包的概念:闭包就是一段代码块,可以命名可以被调用,使用和方法类似

//1 定义一个闭包,闭包的定义就是这么简单
//  就是一段代码块,可以命名,可以被调用
def closer = {
    println "hello groovy"
}

//2 闭包的两种调用方式
//  建议使用第一种方式调用,这样不会和方法的调用混淆
closer.call()
closer()
//输出
hello groovy
hello groovy

6.1.2 闭包的参数:普通参数和隐式参数 闭包是可以传参数的,有点像Java中的lambda表达式,如下代码

//1 定义一个无参的闭包
//  -> 也可以省略, -> 前面的是参数,-> 后面的是闭包体
def closer = {
    -> println "hello groovy"
}

//使用
closer.call()
closer()
//2 定义一个有参的闭包, 参数是name
def closer = {
   String name -> println "hello $name"
}

closer.call()   //参数可以不传,不传就是null
closer.call('world')
closer('android')

闭包还有一个隐式的参数 it ,类似类中的this,如果定义闭包没有明确指定参数,但是调用了闭包传了参数,那么就可以在代码中使用 it 关键字,如下

//3 定义一个无参的闭包, 使用隐式的参数关键字 it
def closer = {
    println "hello $it"
}

closer.call()   //参数可以不传,不传就是null
closer.call('world')
closer('android')

6.1.3 闭包的返回值:总是有返回值的 闭包总是有返回值的,如果没有明确的return,返回值就决定在最后一句代码,如下

//4 闭包的返回值,如果没有return,最后一句话就是返回值
def closer = {
    "hello world"
}

//返回值是"hello world"
def result = closer.call()
println result
//输出
hello world

6.2 闭包的使用

闭包的使用主要从以下几个方面讲

  • 1 与基本类型的结合使用
  • 2 与String结合使用
  • 3 与数据结构结合使用
  • 4 与文件等结合使用

6.1.1 与基本类型的结合使用

//用来求指定number的阶乘
//求5的阶乘 1*2*3*4*5 = 120
int res = fab(5)
println res

int fab(int number){
    int result = 1
    1.upto(number,{
        num -> result *= num
    })

    return result
}

这段代码是不是很简洁,主要的应该都在upto()方法中,我们来看一下upto()方法的源码,如下

  public static void upto(Number self, Number to, @ClosureParams(FirstParam.class) Closure closure) {
        int self1 = self.intValue();    //初始值,就是1
        int to1 = to.intValue();        //结束值,就是5
        if (self1 <= to1) {//如果初始值小于结束值,就循环
            //开始从 self1到to1的循环,把每一个值都交给最后一个闭包来处理
            //而在我们的闭包中,又把每一项乘的结果保存在了result变量中
            for (int i = self1; i <= to1; i++) {
                closure.call(i);
            }
        } else
            throw new GroovyRuntimeException("The argument (" + to +
                    ") to upto() cannot be less than the value (" + self + ") it's called on.");
    }

注意,Groovy方法中,如果最后一个参数是闭包,那么圆括号是可以省略的,我们用downto()方法来演示省略圆括号的用法

既然有upto()方法,那么肯定也有downto()方法了,我们来看一下downto()方法的使用,

最后一个参数是闭包的话,方法的圆括号可以省略

//求5的阶乘 1*2*3*4*5 = 120
int res = fab2(5)
println res

//注意,最后一个参数是闭包的话,方法的圆括号可以省略
int fab2(int number){
    int result = 1
    number.downto(1){
        num -> result *= num
    }

    return result
}

同样,downto()的源码如下:

//与upto()源码类似
 public static void downto(Number self, Number to, @ClosureParams(FirstParam.class) Closure closure) {
        int self1 = self.intValue();    //初始值是5
        int to1 = to.intValue();        //结束值是1
        if (self1 >= to1) {
            for (int i = self1; i >= to1; i--) {
                closure.call(i);
            }
        } else
            throw new GroovyRuntimeException("The argument (" + to +
                    ") to downto() cannot be greater than the value (" + self + ") it's called on.");
    }

再看一个例子,方法只有一个闭包参数的,如下,求和

//求10以内的和
int res = fab3(10)
println res

int fab3(int number){
    int result = 0

    //圆括号省略,直接跟一个闭包
    number.times {
        num -> result += num
    }

    return result
}

//输出
45

同样,times()方法的源码如下:

  public static void times(Number self, @ClosureParams(value=SimpleType.class,options="int")  Closure closure) {
        for (int i = 0, size = self.intValue(); i < size; i++) {
            closure.call(i);
            if (closure.getDirective() == Closure.DONE) {
                break;
            }
        }
    }

从上面几个例子可以知道,我们在写闭包的时候,有时候并不知道需要传入什么样的参数,这个时候就只能去查看源码或者官方文档了,所以查看源码和官方文档是一个比较好的习惯

6.1.2 与String的结合使用

//each()方法的使用

String str = 'hello world'
//把每一个字符都传给闭包
str.each {
    temp -> print temp
}
//输出
hello world
//find()方法的使用

String str = 'tom has 63 books and 3 apples'

//查找第一个是数字的字符,闭包必须返回一个boolean值
def result = str.find {
    word -> word.isNumber()
}

println result
//输出
6

可以看下find()函数是如何工作的,find()的源码如下 :

  public static Object find(Object self, Closure closure) {
        //把闭包包装成了一个BooleanClosureWrapper对象 
        BooleanClosureWrapper bcw = new BooleanClosureWrapper(closure);

        //遍历字符串,并把遍历的每一个字符串都交给闭包处理
        for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext();) {
            Object value = iter.next();

            //如果闭包返回true,说明符合条件,就把value返回
            if (bcw.call(value)) {
                return value;
            }
        }
        return null;
    }

同理还有findAll()方法,用法如下

String str = 'tom has 63 books and 3 apples'

//查找所有是数字的字符
def result = str.findAll {
    word -> word.isNumber()
}

println result
//输出
[6, 3, 3]
//any()方法的使用

String str = 'tom has 63 books and 3 apples'

//判断字符串中是否包含数字
def result = str.any {
    temp -> temp.isNumber()
}

println result
//输出
true

any()方法的工作原理如下:

  public static boolean any(Object self, Closure closure) {
        BooleanClosureWrapper bcw = new BooleanClosureWrapper(closure);
        for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext();) {

            //只要闭包条件满足,就返回true,很简单吧
            if (bcw.call(iter.next())) return true;
        }
        return false;
    }
//every()的用法 

String str = 'tom has 63 books and 3 apples'

//判断字符串中是否全是数字
def result = str.every {
    temp -> temp.isNumber()
}

println result
//输出
false

every()方法的原理如下:

  public static boolean every(Object self, Closure closure) {
        BooleanClosureWrapper bcw = new BooleanClosureWrapper(closure);
        for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext();) {

            //只要有一项不满足,立马返回
            if (!bcw.call(iter.next())) {
                return false;
            }
        }
        return true;
    }

any()方法是只要有一项满足就返回, every()方法是只要有一项不满足就返回,正好相反

//collect()的使用

String str = 'hello world'

//将字符串转化成大写
def result = str.collect {
    temp -> temp.toUpperCase()
}

println result
//输出
[H, E, L, L, O,  , W, O, R, L, D]

collect()方法的工作原理如下:

public static <T> List<T> collect(Object self, Closure<T> transform) {
        //注意第二个参数,new了一个新的ArrayList()
        return (List<T>) collect(self, new ArrayList<T>(), transform);
    }

 public static <T> Collection<T> collect(Object self, Collection<T> collector, Closure<? extends T> transform) {
        for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext(); ) {

            //将闭包处理过的结果都添加到新的ArrayList中
            collector.add(transform.call(iter.next()));
        }

        //返回闭包处理过的结果
        return collector;
    }

6.1 闭包的进阶使用

  • 1 闭包的关键字变量 this, owner, delegate
  • 2 闭包的委托策略

6.1.1 闭包的三个关键字 this, owner, delegate

首先先打印出这三个变量,看看输出什么

def myScript = {
    println "myScript this=" + this
    println "myScript owner=" + owner
    println "myScript delegate=" + delegate
}

myScript.call()
//输出
myScript this=variable.closer@48e4374
myScript owner=variable.closer@48e4374
myScript delegate=variable.closer@48e4374

可以看到,这三个代表的是一个意思,那么这三个关键字到底有什么区别呢?

  • 1 this :代表闭包定义处的类
  • 2 owner :代表闭包定义处的类或者对象
  • 3 delegate:代表任意对象,但是默认是和owner是一样的

如果在闭包中再定义一个闭包呢,看看这三个关键字会打印出什么

def myScript = {
    println "myScript this=" + this
    println "myScript owner=" + owner
    println "myScript delegate=" + delegate

    def innerCloser = {
        println "innerClouser this =" + this
        println "innerClouser owner=" + owner
        println "innerClouser delegate=" + delegate
    }

    innerCloser.call()
}


myScript.call()
//输出
myScript this=variable.closer@3d680b5a
myScript owner=variable.closer@3d680b5a
myScript delegate=variable.closer@3d680b5a
innerClouser this =variable.closer@3d680b5a
innerClouser owner=variable.closer$_run_closure1@3e92efc3
innerClouser delegate=variable.closer$_run_closure1@3e92efc3

所以,总结下来,只有三句话:

  • 1 this,owner,delegate这三个关键字在类和方法中定义时,指向是一样的
  • 2 在闭包中定义的闭包的时候,ownerdelegate指向的是外面的闭包对象
  • 3 在修改了delegate的时候指向的时候,delegate又和其它两个不一样了

如下代码,修改了delegate的指向

//定义了一个内部类
class Person{

}

Person p = new Person()

def myScript = {
    println "myScript this=" + this
    println "myScript owner=" + owner
    println "myScript delegate=" + delegate

    def innerCloser = {
        println "innerClouser this =" + this
        println "innerClouser owner=" + owner
        println "innerClouser delegate=" + delegate
    }

    innerCloser.delegate = p

    innerCloser.call()
}


myScript.call()
//输出
myScript this=variable.closer@5a63f509
myScript owner=variable.closer@5a63f509
myScript delegate=variable.closer@5a63f509
innerClouser this =variable.closer@5a63f509
innerClouser owner=variable.closer$_run_closure1@13e39c73
innerClouser delegate=variable.Person@64cd705f

可以看到delegate指向了Person,不再和owner一样了

6.1.2 this, owner, delegate的作用,也就是委托策略

用一段代码来演示更改委托策略,代码如下:


class Student{
    String name

    //定义一个闭包
    def showMe = {
        return "my name is $name"
    }

    @Override
    String toString() {
        return showMe();
    }
}

class Teacher {
    String name

}

def stu = new Student(name : 'tom')
def tea = new Teacher(name : 'Mrs Li')

//1 没有改变委托策略的情况下
println stu.toString()


//2 改变闭包的委托策略
stu.showMe.delegate = tea
stu.showMe.resolveStrategy = Closure.DELEGATE_FIRST //把委托策略改成先从delegate中找name
println stu.toString()
//输出
my name is tom
my name is Mrs Li

可以看到,在不改变委托策略的情况下,输出的是my name is tom

在改变了委托策略下,把闭包的指向改成了 tea,并且把委托策略也改成了Closure.DELEGATE_FIRST ,那么查找name的时候,首先就从deleage指向处查找 所以打印的是my name is Mrs Li

7 Groovy的数据结构

Groovy中主要有3种常用的数据结构。列表,映射,范围 这三个数据结构比较简单,主要以代码的形式来讲,如下

7.1 列表

//1 定义列表

//1.1 Java的定义方式
def list = new ArrayList()

//1.2 groovy中定义
def list2 = []          //定义一个空的列表
def list3 = [1,2,3,4]   //定义一个非空的列表

println list2.class
println list3.class
//输出
class java.util.ArrayList
class java.util.ArrayList

可以看到,直接这样定义的就是一个ArrayList

//1.2 定义数组

//在groovy中使用as关键字定义数组,注意和列表的区别
def array = [1,2,3,4] as int[]

//或者使用强类型的定义方式
int[] array2 = [1,2,3]

由于在Groovy中,列表和数组的定义方式类似,使用也相似,一般我们只使用列表就够了

//1.3 列表的排序
def list = [-7,5,-3,9,4,0]
list.sort()    //直接一句话就排序了(默认从小到大)
println list

//输出
[-7, -3, 0, 4, 5, 9]
//1.4 列表的排序
def list = [-7,5,-3,9,4,0]

//按照绝对值从大到小排序,闭包中传入的是排序规则 
list.sort {
    a,b -> a == b ? 0 : Math.abs(a) < Math.abs(b) ? 1 : -1
}

println list
//输出
[9, -7, 5, 4, -3, 0]
def stringList = ['a','abc','hello','groovy']

//1.5 按照字符串的长度大小排序
stringList.sort {
    it -> return it.size()
}

println stringList
//输出
[a, abc, hello, groovy]
//1.6 查找列表中的第一个偶数
def list = [-7,5,-3,9,4,0,6]
int result = list.find {
    temp -> return temp % 2 == 0
}

println result
//输出
4

//1.7 查找列表中所有小于0的数
def list = [-7,5,-3,9,4,0,6]
def result = list.findAll {
    temp -> return temp < 0
}

println result
//输出
[-7, -3]
//1.8 统计列表中偶数的个数
def list = [-7,5,-3,9,4,0,6]

int number = list.count {
    return it % 2 == 0
}

println number
//输出
3

7.2 映射

//1.1 映射的定义
def persons = [tom:'北京',jim:'上海',wendy:'天津']

//1.2 映射的使用
println persons['tom']
println persons.get('jim')
println persons.wendy

//1.3 添加元素
persons.xiaoming = '杭州'
println persons

//1.4 groovy中,可以添加一个复杂的元素,比如添加一个map
persons.complex = [a:1,b:2]
println persons

//输出
北京
上海
天津
[tom:北京, jim:上海, wendy:天津, xiaoming:杭州]
[tom:北京, jim:上海, wendy:天津, xiaoming:杭州, complex:[a:1, b:2]]

groovy中的map的常用操作

//1.5 对map的遍历
def students = ['tom':89,'jim':68,'wendy':56,'bob':92]

students.each {
    def student -> println "key is " + student.key + "  value is " + student.value
}

//输出
key is tom  value is 89
key is jim  value is 68
key is wendy  value is 56
key is bob  value is 92
//1.6 对map的遍历,带index
def students = ['tom':89,'jim':68,'wendy':56,'bob':92]

students.eachWithIndex { def student, int index ->
    println "key=${student.key}  value=${student.value} index=${index}"
}

//输出
key=tom  value=89 index=0
key=jim  value=68 index=1
key=wendy  value=56 index=2
key=bob  value=92 index=3
//1.7 直接对map的遍历
def students = ['tom': 89, 'jim': 68, 'wendy': 56, 'bob': 92]

students.each {
    key, value -> println "key=${key},value=${value}"
}

//输出
key=tom,value=89
key=jim,value=68
key=wendy,value=56
key=bob,value=92
//1.8 查询第一个及格的人
def students = ['tom': 89, 'jim': 68, 'wendy': 56, 'bob': 92]


def result = students.find {
    student -> return student.value > 60
}

println result
//输出
tom=89
//1.9 查询所有及格的人
def students = ['tom': 89, 'jim': 68, 'wendy': 56, 'bob': 92]

def result = students.findAll {
    student -> return student.value > 60
}

println result

//输出
[tom:89, jim:68, bob:92]
//2.0 查询及格的人数

def students = ['tom': 89, 'jim': 68, 'wendy': 56, 'bob': 92]

def number = students.count {
    student -> student.value > 60
}

println number

//输出
3
//2.1 查询所有及格的人的名字
def students = ['tom': 89, 'jim': 68, 'wendy': 56, 'bob': 92]

def names = students.findAll {
    student -> student.value > 60
}.collect {
    return it.key
}

println names

//输出
[tom, jim, bob]

7.2 范围

范围的定义

//1.1 范围的定义
def range = 1..10

//1.2 范围的使用
println range[0]            //第一个元素的值
println range.contains(7)   //是否包含7
println range.from          //范围的起始值
println range.to            //范围的结束值

//输出
1
true
1
10

范围就是这么简单,轻量级的list

8 Groovy的面向对象

  • 1 Groovy中类的方法,变量,默认的都是public
  • 2 接口和Java中的几乎一样,只有一点区别,就是只能定义public的方法,不能用protected
//1.1 类的定义
class Person {
    String name
    int age

    //方法的定义
    def addYear(int age){
        this.age += age
    }

    @Override
    String toString() {
       return "name=${name} age=${age}"
    }
}
//1.2 实例化一个类的对象
def person = new Person()
def person1 = new Person(name:'tom',age:23)
def person2 = new Person(name:'tom')

println person.toString()
println person1.toString()
println person2.toString()

//输出
name=null age=0
name=tom age=23
name=tom age=0
//1.3 get/set方法
//无论是直接用 . 还是调用get/set方法,最终都是调用的get/set方法
//这是和Java不一样的地方,是编译器自动为我们生成的get/set
def person = new Person(name:'tom',age:23)

println "name=${person.name} , age=${person.age}"
println "name=${person.getName()}, age=${person.getAge()}"

//输出
name=tom , age=23
name=tom, age=23

8.1 Groovy中的元编程

元编程就是代码在执行过程中运行的时期,Groovy中,调用类的一个方法,如下 :

  • 1 类中是否有此方法,有则调用,如果没有
  • 2 从 MetaClass中查找是否有此方法,有则调用MetaClass中的方法,如果没有
  • 3 是否重写了methodMissing()方法,有则调用methodMissing()方法,如果没有
  • 4 是否重写了invokeMethod()方法,有则invokeMethod()方法,如果没有,throw MissingMethodException

主要是: 类 --> MetaClass --> methodMissing() --> invokeMethod()

java中如果类中没有这个方法,就直接报错了,但是Groovy中,运行时是非常强大的

下面用代码来演示:如下

//先定义一个类,还是以刚才的类定义为例
class Person {
    String name
    int age

    def addYear(int age){
        this.age += age
    }

    @Override
    String toString() {
       return "name=${name} age=${age}"
    }
}

def person = new Person(name:'tom',age:23)

//调用一个没有的方法
//会报 groovy.lang.MissingMethodException 异常
person.show()

//输出
Caught: groovy.lang.MissingMethodException: No signature of method: variable.Person.show() is applicable for argument types: () values: []
//重写invokeMethod()方法

class Person {
    String name
    int age

    def addYear(int age){
        this.age += age
    }

    @Override
    String toString() {
       return "name=${name} age=${age}"
    }

    //重写invokeMethod()方法,name是调用的方法的名字,args是调用方法传的参数,一个方法找不到的时候,调用它代替
    @Override
    Object invokeMethod(String name, Object args) {
        println "the method is ${name},the param is ${args}"
    }
}

//在另一个文件中,调用下面的代码 
def person = new Person(name:'tom',age:23)

//调用一个没有的方法
person.show()

//输出
the method is show,the param is []

所以,由此可知,如果类中没有方法,但是重写了invokeMethod()方法,groovy是不会报错的,会调用invokeMethod()方法,可以在这里面进行提示开发者,没有这个方法

//重写methodMissing()方法
class Person {
    String name
    int age

    def addYear(int age){
        this.age += age
    }

    @Override
    String toString() {
       return "name=${name} age=${age}"
    }

    //重写invokeMethod()方法,name是调用的方法的名字,args是调用方法传的参数
    //一个方法找不到的时候,调用它代替
    @Override
    Object invokeMethod(String name, Object args) {
        println "the method is ${name},the param is ${args}"
    }

    //这个方法的优先级要高于invokeMethod(),有了这个方法,将不会再调用invokeMethod()方法
    def methodMissing(String name,Object args){
        println "the method ${name} is missing"
    }
}
//在另一个文件中,调用下面的代码
def person = new Person(name:'tom',age:23)

//调用一个没有的方法
person.show()

//输出
the method show is missing

8.2 Groovy中的MetaClass

MetaClass可以在运行时为类动态添加属性

//为类动态添加一个属性
Person.metaClass.sex = '男'

def person = new Person(name:'tom',age:23)
println person.sex
//输出
男
//为类动态添加一个方法
Person.metaClass.showAge = {
    -> println age
}

def person = new Person(name:'tom',age:23)
person.showAge()
//输出
23
//为类动态添加一个静态方法
Person.metaClass.static.createPerson = {
    name,age -> new Person(name:name,age:age)
}

def person = Person.createPerson('wendy',44)
println person

//输出
name=wendy age=44

Groovy这种动态给类添加属性和方法的特性可以不用通过重写类而添加类的功能

到现在,Groovy就讲完了,Groovy的用法还需要较多的练习才能记得牢 为学习gradle打下坚实的基础。

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Stella981 Stella981
2年前
Gradle的安装与配置及Spring源码导入(亲测)
前言我这边使用的gradle版本是:gradle6.5.1idea版本是:ideaIU2019.3.1jdk:1.8Gradle简介Gradle是源于ApacheAnt和ApacheMaven概念的项目自动化构建开源工具,它使用一种基于Groovy的的特定领域语言(
Stella981 Stella981
2年前
Gradle的基本使用
Gradle的介绍Gradle是一个基于ApacheAnt和ApacheMaven概念的项目自动化建构工具。它使用一种基于Groovy的特定领域语言(DSL)来声明项目设置,抛弃了基于XML的各种繁琐配置。面向Java应用为主。当前其支持的语言限于Java、Groovy和Scala,计划未来将支持更多的语言。Gra
Stella981 Stella981
2年前
Gradle之介绍
Gradle是基于JVM构建工具的新一代版本。它从现有的构建工具如Ant和Maven中学到了很多东西,并且把它们的最优思想提升到更高层次。遵循基于约定的构建方式,Gradle可以用一种声明式的方式为你的问题领域建模,它使用一种强大的且具有表达性的基于Groovy的领域特定语言(DSL),而不是XML,因为Gradle是基于JVM的,它允许你使用自己最喜欢的J
Stella981 Stella981
2年前
Android蓝牙连接汽车OBD设备
//设备连接public class BluetoothConnect implements Runnable {    private static final UUID CONNECT_UUID  UUID.fromString("0000110100001000800000805F9B34FB");
Wesley13 Wesley13
2年前
00_设计模式之语言选择
设计模式之语言选择设计模式简介背景设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结。设计模式(Designpattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
3个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这