• Gradle系列之一 Groovy语法精讲


    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

    2. 对象类型:和Java的一样

    • - 变量的定义
    1. 强类型定义:如 int a=10 ,指定变量的类型
    2. 弱类型定义:如 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打下坚实的基础。

    欢迎访问作者的helloworld的个人博客:
    https://www.helloworld.net/jiulu

    同时也可以加作者的微信:daitukeji
    也可以扫下面的二维码添加
    image

  • 相关阅读:
    黑盒测试分类
    Java变量的初始化顺序
    javahttp请求四种方式
    mybatis sql查询慢
    jacoco搭配springMVC maven tomcat项目,单元测试&接口测试踩坑
    maven标签pluginManagement和plugins区别
    Java反射学习之Field类访问和修饰变量
    Classpath重新认识
    properties文件加载的六种方法
    VMware ESXI6.0安装和配置IP地址
  • 原文地址:https://www.cnblogs.com/start1225/p/9940084.html
Copyright © 2020-2023  润新知