Groovy 之基础语法,闭包

基本类型

​ 其实在 Groovy 中是没有基本类型的,例如下所示:

int x = 10;
println(x.class)

​ 打印结果如下:

class java.lang.Integer

​ 可以看到他是一个 Integer 对象。其实在底层编译的时候已经将 int 装箱成了 对象。

变量的定义

​ groovy 定义变量有两种方式,分别是 强类型定义 和 若类型定义,强类型定义和 java 中的意义,但是弱类型定义是什么呢,如下:

//强类型定义方式
int x = 10;
println(x.class)

//弱类型定义方式
def x1 = 1;
println(x1.class)
def x2 = 3.14;
println(x2.class)
def x3 = "Android"
println(x3.class)

​ 结果如下:

class java.lang.Integer
class java.lang.Integer
class java.math.BigDecimal
class java.lang.String

​ 第二行 是大数据类型

​ 其实弱类型定义就是不指定具体的类型。通过值来确定它到底是什么类型。用 def 来定义

​ 如何选择呢?如果在本类中使用,则建议使用 def 类型。但是别的类也要用到这个变量。则尽量使用强类型定义;def 声明的变量类型可以随时改变,如 x1 是 Integer 。如果给 x1 赋值为 String,这样他就会变为 String。正是这个原因所以才不建议在 别的类使用这个变量时定义为 def。

字符串

​ 常用的定义方式:

//1,通过 单引号定义String ,和双引号没有任何区别 ,转义 '单引号 \'String \''
def name = '单引号 String'
println(name)


//2,通过三个单引号定义String ,但定义的格式进行输出,
def name1 = '''第一行
第二行
第三行'''
println(name1)

// 3,双引号 ,可扩展字符串,扩展后使用的不是String,而是GString类
def name2 = "双引号"
def hello = "hello ${name2}"
println(name2)
println(hello.class)

//可扩展字符串使用 ,可以扩展任意的表达式
def sum = "sum = ${5 - 6 - 6 + 12}"
println(sum)

def result = echo(sum)
println(result)
String echo(String message){
    return message;
}

通过上面这几种方式可以 定义字符串,其实一般最常用的就是 第三种了。注意扩展后的字符串就不是 String。而是 GString。不信的话可以打印看一下。

字符串方法

字符串的方法
1,第一种就是String类原有的方法
2,第二种则是 DefaultGroovyMethods ,是 Groovy 对所有对象的扩展
3,第三种是 StringGroovyMethods ,继承自 DefaultGroovyMethods 。

重点要看的是 StringGroovyMethods 。他继承自第二种,第一种是 String,比较熟悉。

首先定义一个字符串:

String str = "哈哈哈"
  • 填充

    println str.center(8, 'a') //填充: aa哈哈哈aaa
    println str.padLeft(8, 'a') //填充:aaaaa哈哈哈
    
  • 比较

    println str > "嘻嘻嘻" //通过运算符比较字符串大小 :false
    
  • 索引

    println str.getAt(0) //下标为0 :哈
    println str[0]  //通过类似于数组的方式来获取 :哈
    println str[0..1] //指定范围 :哈哈
    
  • 减法

    println str.minus("哈哈") //减法 : 哈 	--减去两个剩一个 。
    println str - "哈哈";
    
  • 倒序

    println str.reverse()
    
  • 首字母大写

    println str.capitalize()
    
  • 判断 str 是否为数字类型

    println str.isNumber()
    
  • 转为基本数据类型

    println str.toInteger()
    println str.toBoolean()
    println str.toDouble()
    
  • 其实方法还有很多,这里就不一一列举了。

    可查看文档

控制语句

​ switch:groovy 中的 switch 和 java 中的有明显的不同,它可以接受任意的数据类型。如下:

// switch ,可以传入任意类型的数据
def x = 1.23
def result;
switch (x) {
    case '哈哈哈哈':
        result = '0'
        break
    case '嘻嘻':
        result = '1'
        break
    case [4, 5, 6, 'inlist']: //列表
        result = '2'
        break
    case 12..30: //范围
        result = '3'
        break
    case Integer:
        result = '4'
        break
    case BigDecimal:
        result = '5'
        break
    default:
        result = 'defalut'
        break
}
println result //5

​ for 循环

// 对范围的 for 循环
def sum = 0
for (i in 0..9) {
    sum += i
}
println sum

//对 list 进行循环
sum = 0
for (i in [1, 2, 3, 4, 5, 6, 7, 8, 7]) {
    sum += i
}
println sum

//对 map 进行循环
sum = 0;
for (i in ['one': 1, 'two': 2, "three": 3]) {
    sum += i.value
}
println sum; 
// 45
// 43
// 6

闭包

基础详解

​ 闭包是一个开放的,匿名的代码块,可以带参数,返回一个值。可以通过一个变量引用到他,也可以传递给别人进行处理。

  • 闭包的创建和调用

    def clouser = {
        println "hello groovy"
    }
    //两种方式调用闭包
    clouser.call()	// hello groovy
    clouser()		// hello groovy
    
  • 创建带参数的闭包和调用

    def clouser = {
            //箭头前面是参数,后面是闭包体
        String name, int age ->
            println "hello groovy ${name} -- ${age}"
    }
    //调用带参数闭包
    clouser.call("张三", 32)  // hello groovy 张三 -- 32
    clouser("李四", 23)		//hello groovy 李四 -- 23
    
  • 默认参数

    //每一个闭包都有一个默认的 it 参数,如果不想使用,则显示的定义即可。
    def clo = {
        println "hello ${it}"
    }
    clo.call("李四")	 //hello 李四
    
  • 闭包返回值

    def clouser = {
            //箭头前面是参数,后面是闭包体
        String name, int age ->
            return "hello groovy ${name} -- ${age}"
    }
    //调用带参数闭包
    println clouser.call("张三", 32) //hello groovy 张三 -- 32
    

使用详解

​ 基本类型结合使用:


  • 阶乘

    int x = 5
    println fab(x)
    //求 number 的阶乘
    int fab(int number) {
        int reslut = 1
        //调用 upto 方法,传入 number 和 闭包 ,闭包中有一个 num 参数
        1.upto(number, { num -> reslut *= num })
        return reslut
    }
    //结果:120
    

    为什么要这样写呢?看一下源码秒懂!

    //打眼一看这里需要三个参数,我们只传了两个?,注意:1.upto()中的 1,代表的是第一个参数,
    public static void upto(Number self, Number to, @ClosureParams(FirstParam.class) Closure closure) {
    		//拿到开始的位置和结束的位置
            int self1 = self.intValue();
            int to1 = to.intValue();
            //小于等于
            if (self1 <= to1) {
                for (int i = self1; i <= to1; i++) {
                	//调用传入的闭包,将i 传进去,从这里就可以知道我们写的闭包为啥要一个 num 的参数了。
                    closure.call(i);
                }
            } else
                throw new GroovyRuntimeException("The argument (" + to +
                        ") to upto() cannot be less than the value (" + self + ") it's called on.");
        }
    
  • 阶乘2

    int fab2(int number) {
        int result = 1
        number.downto(1) {
            num -> result *= num
        }
        return result
    }
    //结果:120
    

    你知道 downto 方法传入了几个参数吗。。。。没错,是3个,number是一个,1 是一个,括号外面的闭包是一个。闭包写在括号外面和写在里面效果完全一样。这个是倒着来的。具体的可以查看源码。

  • 累加

    int cal(int number) {
        int result
        // times 只有两个参数,所以可以使用这种方式来写 ,具体的实现可以看源码,非常简单
        number.times {
            num -> result += num
        }
        return result;
    }
    

字符串的结合使用


定义一个字符串:

String str = "the 2 and 3 is 5"
  • each 遍历

    str.each {
        String temp ->
            print temp  //the 2 and 3 is 5
    }
    //看一下 each 方法的源码
    public static <T> T each(T self, Closure closure) {
        	//第一个是迭代器,第二个则是闭包了
            each(InvokerHelper.asIterator(self), closure);
            return self;
        }
    
    public static <T> Iterator<T> each(Iterator<T> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure closure) {
        	//遍历 字符串,
            while (self.hasNext()) {
                Object arg = self.next();
                //调用闭包,传入了一个参数,从这里就可以知道我们在定义闭包的时候需要加一个参数
                closure.call(arg);
            }
            return self;
        }
    
  • 查找符合条件的第一个

    println str.find {
        String s ->
        	//返回 boolean 。是否符合条件,这里的条件为:是否是数字
            s.isNumber() 
    }
    //结果:2 ,第一个符合结果的是2,看一下源码
        public static Object find(Object self, Closure closure) {
            //将闭包弄成了 boolean 类型的
            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;
        }
    
    //查找全部
    println str.findAll {
        String s ->
            return s.isNumber()
    }
    //结果:[2, 3, 5] ,查看源码可知找到后会存在集合中,最后进行返回
    
  • 判断是否包含数字

    println str.any {
        String s ->
            return s.isNumber() // true
    }
    //源码可自行查看
    
  • 字符串是否每一项都为数字

    println str.every {
        String s ->
            s.isNumber() //false
    }
    //源码
    public static boolean every(Object self, Closure predicate) {
          return every(InvokerHelper.asIterator(self), predicate);
    }
    public static <T> boolean every(Iterator<T> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure predicate) {
            BooleanClosureWrapper bcw = new BooleanClosureWrapper(predicate);
            while (self.hasNext()) {
                //是否满足闭包中的条件
                if (!bcw.call(self.next())) {
                    return false;
                }
            }
        	//循环完成后表示都满足条件,返回true
            return true;
        }
    
  • 将字符串转为集合

    println str.collect {
        //将 字符转换大写
        it.toUpperCase()
    }
    //查看源码
     public static <T> List<T> collect(Object self, Closure<T> transform) {
         	//这里创建了一个新的集合
          return (List<T>) collect(self, new ArrayList<T>(), transform);
     }
    public static <T> Collection<T> collect(Object self, Collection<T> collector, Closure<? extends T> transform) {
        	//给字符串加上迭代器
            return collect(InvokerHelper.asIterator(self), collector, transform);
    }
      public static <S,T> Collection<T> collect(Iterator<S> self, Collection<T> collector, @ClosureParams(FirstParam.FirstGenericType.class) Closure<? extends T> transform) {
            while (self.hasNext()) {
                //调用闭包中的方法,将返回的结果保存到集合中
                collector.add(transform.call(self.next()));
            }
          	//返回集合
            return collector;
        }
    

进阶详解

  • 闭包关键变量 this,owner,delegate

    this:闭包定义处的类

    owner :代表闭包定义处的类或者对象。因为闭包是可以嵌套的,当闭包嵌套的时候,owner 就是闭包,而不是所处的类

    delegate:任意一个对象,默认与 owner 一样

    看一个例子:

    def clouser = {
        println this  //闭包定义处的类
        println owner  //代表闭包定义处的类或者对象,因为闭包是可以嵌套的,如果 闭包中有嵌套,则 owner 代表的就是闭包
        println delegate //任意一个对象。默认与 owner 一致
    }
    clouser.call()
    //结果:
    variable.ClooseStudy@5acf93bb
    variable.ClooseStudy@5acf93bb
    variable.ClooseStudy@5acf93bb
    

    可以看到这三个结果是一样的。因为这里的闭包没有嵌套,所以 owner 是定义闭包处的类,delegate 则和 owner 一致

    接着看:

    //定义了一个内部类
    class PerSon {
        //静态闭包
        def static classClouser = {
            println "Class  " + this
            println "Class  " + owner
            println "Class  " + delegate
        }
    	//静态方法
        def static say() {
            def classClouser = {
                println "method  " + this
                println "method  " + owner
                println "method  " + delegate
            }
            classClouser.call()
        }
    }
    //结果:
    Class  class variable.PerSon
    Class  class variable.PerSon
    Class  class variable.PerSon
    method  class variable.PerSon
    method  class variable.PerSon
    method  class variable.PerSon
    

    其实这个和上面的都差不多,这里的 this 就是内部类了,而 owner 因为不是嵌套,所以也是 Person。delegate 就不用说了。注意:这里的结果后面没有内存地址是因为 闭包和方法都是静态的。

    接着看:

    //闭包中定义一个闭包
    def nestClouser = {
        def innerClouser = {
            println "innerClouser  " + this
            println "innerClouser  " + owner
            println "innerClouser  " + delegate
        }
        innerClouser.call()
    }
    nestClouser.call()
    //结果:
    innerClouser  variable.ClooseStudy@6cd28fa7
    innerClouser  variable.ClooseStudy$_run_closure2@f0c8a99
    innerClouser  variable.ClooseStudy$_run_closure2@f0c8a99
    

    这里的 this 是当前类,但是 owner 不一样了,可以看到他在后面拼接了一点地址,这个就是外层闭包的对象。delegate 默认和 owner 一样,所以他们两个一样。你可以打印一下 nestClouser 的地址看一下是否和 owner 一样。。

    如果修改了 delegate 的值,则他和 owner 就不一样了。注意:this 和 owner 是不能被修改的,只有 delegate 可以修改

闭包委托策略

​ 首先看一段代码

class Student {
    String name
    def pretty = {
        "My name is ${name}"
    }

    String toString() {
        pretty.call()
    }
}


class Teacher {
    String name
}

def student = new Student(name: '张三')
def teacher = new Teacher(name: "老师")
println student.toString()

​ 定义了两个类,一个学生和一个老师。

​ 如果调用 student 的 toString 方法,会打印什么信息呢?

My name is 张三

​ 可以看到打印的是张三。那如果要打印 Teacher 的 name 呢?这个时候就要用到 委托策略了

student.pretty.delegate = teacher
println student.toString()

​ 将 teacher 传给 student 的闭包的 delegate 。这个时候在输出一下,你会发现没有任何改变。。。

​ 我们少改了一个东西, 默认的委托策略是 OWNER_FIRST ,即从 owner 中开始寻找对象 。owner 指向的是当前类 student。所以才没有发生任何变化。所以我们要修改委托策略:

student.pretty.delegate = teacher
student.pretty.resolveStrategy = Closure.DELEGATE_FIRST //修改委托策略
println student.toString()
//My name is 老师

​ 修改为 DELEGATE_FIRST 后会从 delegate 中开始寻找,因为上面指定了 delegate 是 teacher ,所以输出就变了

​ 修改 Teacher 中的 name 字段名字,如下:

class Teacher {
    String name1
}
def teacher = new Teacher(name1: "老师")

​ 这个时候在运行会发现 运行的结果是 张三 ,因为 student 中的闭包里面用的是 name,而不是 name1,所以找不到 name 后就会寻找当前类的 name。

​ 修改一下委托策略,如下:

student.pretty.resolveStrategy = Closure.DELEGATE_ONLY

​ 接着在运行一下会报错:

name for class: variable.Teacher Possible solutions: name1

​ 在 Teacher 中 没有找到 name ,所以报错。这个策略只会从 delegate 中寻找,而不会从当前类中找

​ 委托策略一共有四种:

public static final int OWNER_FIRST = 0;
public static final int DELEGATE_FIRST = 1;
public static final int OWNER_ONLY = 2;
public static final int DELEGATE_ONLY = 3;

​ 默认的就是 OWNER_FIRST ,即从 owner 中开始寻找。

列表

//列表
def list = [1, 23, 324, -5, 6]  // groovy 中定义的方式,和上面的完全一样 ,并且添加元素。
println list.class

//排序
list.sort()
println list
list.sort {
    a, b ->
        a > b ? 0 : -1
}
println list
//按字符串长度排序
def strList = ['a', 'ab', 'abc', 'aaaaa', 'b', 'cadfa', 'ace']
strList.sort {
    it ->
        return it.size()
}
println strList

//查找被 2 整除
println list.findAll() {
    return it % 2 == 0
}
// 判断是否列表中元素是否都能够被 2 整除
println list.every {
    return it % 2 == 0;
}
println list.min() //最小值
println list.max() //最大值
println list.min { Math.abs(it) } //绝对值 最小值
println list.max { Math.abs(it) } //绝对值 最小值

println list.count {
    return it % 2  //统计,有多少偶数
}

//添加
list.add(5)
list.leftShift(4)
println list + [4, 5]

//删除
list.remove(0) //删除下标为 0 的元素
println list
list.remove((Object) 4) //删除内容为 4 的元素
println list
println list.removeAt(0) //删除指定位置元素,并返回此元素
list.removeElement(0) //删除
println list - [1, 23] //删除 1 和 23

上面是列表的定义和常用的方法

有没有感觉和数组的定义一样。当然数组的定义方式也变了,如下:

//数组
def array = [1, 3, 4, 5] as int[]   
// groovy 中 数组的定义
int[] array2 = [13, 4, 5, 6]  //强类型数组定义

Map

//定义
def map = [red: '红色', yellow: '黄色', black: '黑色']

//查找
println map.get('red')
println map['yellow']
println map.black
println map.find {
    it.getValue().equals("红色")
}
//计数
println map.count {
    it.value.equals('黄色')
}
println map.findAll {
    return it.key.equals('red') //key 为 red
}.collect {
    it.value  //key 为 red 的value
}

//分组
println map.groupBy {
    return it.value.equals('红色') ? '红色' : '其他颜色'
}


//添加
map.put('blue', '绿色')
map.green = '灰色'
map.data = [a: 0, b: 1];
println map
println map.getClass() //默认是 LinkerHash , 在定义的时候 通过 as HashMap 可转为 HashMap

//遍历
map.each {
    def m ->
        println m.key + "----" + m.value
}
//带下表的变量
map.eachWithIndex { Map.Entry<String, String> entry, int i ->
    println "index ${i}" + entry.key + "----" + entry.value
}
map.each {
    key, value ->
        println key + "----" + value
}

//排序
println map.sort()
println map.sort {
    s1, s2 ->
        return s1.key < s2.key ? 0 : -1
}

以上为最常见的使用

范围

//定义
def range = 1..29

println range[0]//获取
println range.contains(15) //是否包含
println range.from //开始
println range.to    //结束

//遍历
range.each {
    print it
}
println ""
for (i in range) {
    print i
}
println ""

//在 switch 中使用
def sh = {
    int x ->
        def result = 0;
        switch (x) {
            case 0..10:
                result = 1;
                break
            case 10..20:
                result = 2;
                break
            case 20..30:
                result = 3;
                break
        }
        return result;
}
println sh.call(20)
public interface Range<T extends Comparable> extends List<T> {
	......
}

其实 Range 是 继承自 List。所以 List 有的方法他都有。

原创文章 119 获赞 77 访问量 3万+

猜你喜欢

转载自blog.csdn.net/baidu_40389775/article/details/103661413