阅读 257

Groovy窥探-入门语法2

书接上文,我们继续讲解Groovy的基础语法部分

  • 逻辑条件
    在Groovy中的逻辑和Java中的一样,大致分为3类:
    1. 顺序逻辑-单步往下执行
    2. 条件逻辑-if/else switch/case
    3. 循环逻辑-while循环、for循环

重点讲述的是switch/case逻辑和for循环

part1: switch/case

def x=1.23
def result
switch(x){
  case 'foo':
    result='found foo'
    break
  case 'bar':
    result='bar'
    break
  case [4,5,6,'inlist']://List 由[]定义,其元素可以是任何对象
    result='list'
    break
  case 12..30://范围
    result='range'
    break
  case Integer:
    result='integer'
    break
  case BigDecimal:
    result='big decimal'
    break 
  defalut
    break
}
复制代码

在java中switch/case只能传入int类型、char类型 、String类型、枚举类型,但是在groovy中的switch/case可能匹配到任何类型的数据,简单的说可以替换if/else

当然也可以改造上面的代码如下

def x=1.23
def result
switch(x.class){
    ...
  defalut
    break
复制代码

可以通过x.class来判断数据类型,比如是否为Integer、Double类型的数据,相当于是java中的instance of的类型判断

part2: for循环

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

sum=0
//对List的循环 groovy中的List的写法和Java中的数组写法类似,List 由[]定义,其元素可以是任何对象
for(i in [1,2,3,4,5,6,,8,9]){
    sum+=i
}
println sum

sum=0
//对Map进行循环 java中需要通过获取迭代器后进行操作,但是在groovy中已经处理过可以省略该操作
for(i in ['lili':1,'luck':2,'xiaoming':3){
    sum++i.value
}
println sum
复制代码

所以相比Java而言,groovy中的逻辑控制看起来比Java中的要更强大,功能更丰富。

  • 闭包(重点)
    通过和Java语法的比较,接下来我们继续学习下闭包的知识。闭包是groovy中的最强大的特性之一。
    通过下面的几部分让我们更好的了解闭包这个特性
    1. 闭包的基础
      1.1 闭包概念
      闭包就是一段代码块,使用起来和方法有点类似,但是又和方法有区别
          //闭包的定义
          def clouser1={println 'Hello Groovy'}
          clouser1.call() //调用闭包
          clouser1()//调用闭包
      复制代码
      1.2 闭包参数
      闭包和方法一样也可以和方法一样添加参数,执行具体内容语句
      //闭包的参数使用
      //单个参数
      def clouser={String name ->println "Hello ${name}"}
      clouser.call('groovy') //调用闭包
      clouser(‘peter’)//调用闭包
      def myname='wangwu'
      clouser(myname)//调用闭包
      
      
      //两个参数,多个参数的写法则为在箭头左侧使用逗号分割添加想要添加的参数
      def clouser2={String name,int age ->println "Hello ${name},My age is ${age}"}
      clouser2(‘peter’,20)//调用闭包
      
      //隐式参数 it
      def clouser3={String name, ->println "Hello ${it}"}
      clouser3(‘peter’)//调用闭包,同样可以输出Hello peter
      复制代码
    复制代码
String name->println 'Hello Groovy'
复制代码

以箭头为分割,箭头之前就是闭包的参数部分,箭头后面的就是闭包体内容,如遇到如下代码,则表示参数为空

->println 'Hello Groovy'
复制代码

1.3 闭包返回值
Java中的方法有两种返回类型,一种是有返回值,一种是无返回值,Groovy的函数里,可以不使用return xxx 来设置 xxx 为函数返回值。如果不使用 return 语句的话,则函数里最后一句代码的执行结果被设置成返回值

        def getSomething(){
           "getSomething return value" //如果这是最后一行代码,则返回类型为String
            1000 //如果这是最后一行代码,则返回类型为 Integer
       }
       print getSomething() //结果:1000
复制代码

但是在groovy闭包中一定会有返回值,如果闭包体里面没有return语句的时候,返回的就是null

//闭包返回值
def clouser={String name ->println "Hello ${name}"}
def myname='wangwu'
def result=clouser(myname)//调用闭包 
println result//结果为null

def clouser1={String name ->return  "Hello ${name}"}
def result1=clouser1(myname)//调用闭包 
println result1//结果为Hello wangwu 
复制代码
  1. 闭包使用
    2-1. 结合基本类型简单使用
//求指定number的阶乘
//方法一 :upto
int fab(int number){
 int result=1
 //1是从1开始,到number,1属于Integer类型,所以可以直接调用
 1.upto(number,{num->result*=num})
 return result
}

//方法二 :downto
int fab2(int number){
 int result=1
    //grovvy中闭包可以不放在括号内,可以直接放在括号外.像下面这个写法
number.downto(1){num->result*=num}
 return result
}

//方法三 :times累加操作
int sum(int number){
 int result
    //times方法的参数也是闭包,闭包写在括号外是groovy中很常见的方式.times方法的实现循环始终是从0开始的
  number.times {num->result +=num}
 return result
}

int x=fab(5)
println(x) //输出结果:120

int y=fab2(5)
println(y) //输出结果:120

int z=sum(101)//因为times方法中是小于,所以参数+1
println(z) //输出结果:5050
//闭包传递参数类型及个数不知道时,需要查看源码方法中是如何调用的
复制代码

Q:为什么没有使用循环,上面的方法都能实现阶乘的效果?
A:因为upto、downto、times里面的源码实现了for循环的操作,详见DefaultGroovyMethods源码类中的解析

  public static void upto(Number self, Number to, @ClosureParams(FirstParam.class) Closure closure) {
        int self1 = self.intValue();
        int to1 = to.intValue();
        if (self1 > to1) {
            throw new GroovyRuntimeException("The argument (" + to + ") to upto() cannot be less than the value (" + self + ") it's called on.");
        } else {
            for(int i = self1; i <= to1; ++i) {
                closure.call(i);
            }

        }
    }

复制代码
   public static void downto(Number self, Number to, @ClosureParams(FirstParam.class) Closure closure) {
        int self1 = self.intValue();
        int to1 = to.intValue();
        if (self1 < to1) {
            throw new GroovyRuntimeException("The argument (" + to + ") to downto() cannot be greater than the value (" + self + ") it's called on.");
        } else {
            for(int i = self1; i >= to1; --i) {
                closure.call(i);
            }

        }
    }

复制代码
//times中的循环是i < size,所以需要+1
   public static void times(Number self, @ClosureParams(value = SimpleType.class,options = {"int"}) Closure closure) {
        int i = 0;

        for(int size = self.intValue(); i < size; ++i) {
            closure.call(i);
            if (closure.getDirective() == 1) {
                break;
            }
        }

    }
复制代码

2-2. 闭包与String结合使用(结合源码进行学习)

String str='the 2 and 3 is 5'
//each遍历
str.each {
    String temp->print temp.multiply(2)
}
//输出结果:tthhee  22  aanndd  33  iiss  55
each方法的返回值就是返回调用者的本身
print str.each {
//    String temp->print temp.multiply(2)
}
//输出结果:the 2 and 3 is 5


//find来查找符合条件的第一个直接输出
print str.find{
    String s->s.isNumber()
}
// 输出结果:2

//findAll来查找所有,添加符合条件的内容
def list=str.findAll{
    String s->s.isNumber()
}
print list.toListString() // 输出结果:[2, 3, 5]


//字符串是否满足某种条件,查找到一个后返回true,否则是false
def  result=str.any {
    String s->s.isNumber()
}
print result //输出结果:true

//字符串是否满足某种条件,查找到所有的都满足后返回true,否则是false
def  result=str.every {
    String s->s.isNumber()
}
print result //输出结果:false

//collect:将字符串转换成List的结果
def collList=str.collect {
    it.toUpperCase()
}
print collList.toListString()  //输出结果:[T, H, E,  , 2,  , A, N, D,  , 3,  , I, S,  , 5]
复制代码
    2-3. 闭包与数据结构的使用(在后面的数据结构的时候再进行学习)   
    2-4. 闭包与文件的使用(在后面学习文件的时候再进行学习)     
复制代码
  1. 闭包进阶知识
    3-1:闭包强大的核心-关键字变量(this、owner、delegate)
    首先看一段代码
def  scriptClouser={
    println 'script this:'+this
    println 'script owner:'+owner
    println 'script delegate:'+delegate
}
scriptClouser.call()
复制代码

输出的结果

script this:grammer@2a54a73f
script owner:grammer@2a54a73f
script delegate:grammer@2a54a73f
复制代码

Q:嗯?从上面的代码看,这三个输出的结果不是都是一个东西吗?区别在哪里?
A:答案如下
this:在java中的表明当前类的方法或者是变量,groovy中也是如此,定标闭包定义处的类,实例或者是类本身
owner:代码闭包定义处的类或者是对象,也就是说如果闭包中嵌套了闭包,那个owner表示的就是闭包内部的对象,而不再是当前类
delegate:任意一个第三方对象,默认指向了owner的对象 代码验证下:

class Person{
    //static 指向了Person
    def  static  classClouser={
        println 'classClouser this:'+this //Person
        println 'classClouser owner:'+owner //classClouser
        println 'classClouser delegate:'+delegate //classClouser
    }

    def   say(){
        def   methodClouser={
            println 'classClouser this:'+this
            println 'classClouser owner:'+owner
            println 'classClouser delegate:'+delegate
        }
        methodClouser.call()
    }
}


//闭包中定义闭包
def nestClouser={
    def innerClouser={
        println 'classClouser this:'+this
        println 'classClouser owner:'+owner
        println 'classClouser delegate:'+delegate
    }
    innerClouser.delegate=p //修改默认的delegate,此时owner和delagate就不一致了
    innerClouser.call()
}
nestClouser.call()
复制代码

3-2 委托策略

//闭包委托策略
class Student {
    String name
    def pretty = { "My name is ${name}" }

    String toString() { pretty.call() }

}

class Teacher{
    String name
}

def stu=new Student(name:'Sarash')
def tea=new Teacher(name:'Qroovy')
println stu.toString() //结果:My name is Sarash

复制代码

如果此时我们需要获取到Teacher类中的属性,那么要怎么办?可以将代码更改如下:

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

    String toString() { pretty.call() }

}
class Teacher{
    String name
}
def stu=new Student(name:'Sarash')
def tea=new Teacher(name:'Qroovy')
stu.pretty.delegate=tea//修改委托
stu.pretty.resolveStrategy=Closure.DELEGATE_FIRST//更改委托策略方式
println stu.toString() ////结果:My name is Qroovy
复制代码

在源码中发现Closure的常见的委托策略有下面四个值

 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,假如此时代码改变成如下的形式又会是什么结果呢?

//闭包委托策略
class Student {
    String name
    def pretty = { "My name is ${name}" }

    String toString() { pretty.call() }

}

class Teacher{
    String name
}

def stu=new Student(name:'Sarash')
def tea=new Teacher(name:'Qroovy')
println stu.toString() //结果:My name is Sarash

复制代码

如果此时我们需要获取到Teacher类中的属性,那么要怎么办?可以将代码更改如下:

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

    String toString() { pretty.call() }

}
class Teacher{
    String name
}
def stu=new Student(name:'Sarash')
def tea=new Teacher(name:'Qroovy')
stu.pretty.delegate=tea//修改委托
stu.pretty.resolveStrategy=Closure.DELEGATE_ONLY//更改委托策略方式
println stu.toString() 
复制代码

猜想下应该是报错,因为在策略中我们并没有发现name1这个属性,现在验证下结果

Caught: groovy.lang.MissingPropertyException: No such property: name for class: Teacher
Possible solutions: name1
groovy.lang.MissingPropertyException: No such property: name for class: Teacher
Possible solutions: name1
	at Student$_closure1.doCall(grammer.groovy:83)
	at Student$_closure1.doCall(grammer.groovy)
	at Student.toString(grammer.groovy:85)
	at Student$toString.call(Unknown Source)
	at grammer.run(grammer.groovy:98)
复制代码

果然,报错也是提示name1这个属性不在策略中,通过上面代码的学习,我们也算是对闭包有了一个了解了,关于这个委托策略,我觉得在普通的代码中可能用的少,但是涉及到框架的时候就用的比较多了。
小伙伴们可以通过自己修改上述代码进行你们的猜想和验证。