Gradle学习系列(一):Groovy学习

作者:renxhui
链接:https://juejin.cn/post/6932778923491065864 

配置Groovy环境

由于Groovy是运行在java虚拟机上的,所以首先要确定你的电脑有java环境

Groovy 语法学习

  • Groovy 的注释和java一样 // 或者/**/
  • Groovy语句可以不用分号结尾,这个其实是为了代码写起来更加简洁
  • Groovy中支持动态类型,就是定义变量的时候可以不定义类型,Groovy中定义变量可以使用def关键字,虽然def不是必须的,但是还是建议使用def,让代码更加清晰
def variable1 = 1 //可以不使用分号结尾
def varable2 = "I am a person"
def int x = 1 //变量定义时,也可以直接指定类型

 

  • 函数定义时,参数的类型也可以不指定
String testFunction(arg1,arg2){//无需指定参数类型 ...
}
  • 除了定义变量不指定类型外,Groovy中函数的返回值也可以是无类型的
//无类型的函数定义,必须使用 def 关键字
def nonReturnTypeFunc(){
last_line //最后一行代码的执行结果就是本函数的返回值
}

//如果指定了函数返回类型,则可不必加 def 关键字来定义函数
String getString(){
return "I am a string"
}
  • 函数返回值:Groovy的函数里,可以不使用ruturn,如果不使用return的话,那么函数中最后一句代码执行结果设置为返回值
//下面这个函数的返回值是字符串"getSomething return value"
def getSomething(){
"getSomething return value" //如果这是最后一行代码,则返回类型为 String
1000 //如果这是最后一行代码,则返回类型为 Integer
}

如果函数指明了返回值的类型,那么就必须返回正确的类型,否则就报错,如果返回值是动态类型,才可以返回任意数据类型

  • Groovy对字符串支持很强大
1 单引号''中的内容严格对应 Java 中的 String,不对$符号进行转义
def singleQuote='I am $ dolloar' //输出就是 I am $ dolloar

2 双引号""的内容则和脚本语言的处理有点像,如果字符中有$号的话,则它会$表达式先求值。
def doubleQuoteWithoutDollar = "I am one dollar" //输出 I am one dollar
def x = 1
def doubleQuoteWithDollar = "I am $x dolloar" //输出 I am 1 dolloar

3 三个引号'''xxx'''中的字符串支持随意换行 比如 
def multieLines = ''' begin
line 1 
line 2 
end '''
  • 最后除了每行代码不用加分号外,Groovy中函数调用的时候还可以不加括号
println("test") ---> println "test"

注意:虽然写代码的时候可以函数调用不带括号,但是函数调用如果是Groovy的API那么可以不带括号,如:print方法,否则还是需要带括号的,因为Groovy对此的支持还是有一些问题的

Groovy中的数据类型

这里我们只介绍和java中不太一样的东西

基本数据类型

作为动态语言,Groovy中所有的事物都是对象,所以int,boolean这些java中的基本数据类型,在Groovy中其实对应的是他们包装数据类型,比如 int - integer,boolean-Boolean

容器类

Groovy中容器其实就三种

  • List:链表,其底层对应java中的List接口,一般用ArrayList作为实现类
  • Map:键-值表,其底层对应java中的LinkedHashMap
  • Range:范围,他其实是List的一种扩展

List

class Example {
   static void main(String[] args) {
       //定义list,可以是任何类型的元素
        def list  = [2,"吃",true]
        //list元素赋值,不用担心数组越界,如果超过长度list会自动向该索引填充
        list[4]="tt"
        //遍历list
        for (a in list) {
            println a;
        }
   }
}

Map

Map由[:]定义,冒号左边是key,右边是value,key必须是字符串,value可以是任何的对象,key可以加'',也可以不加,比如 def map = [key1: "测试",key2: true];这样系统会自动处理成字符串 'key','key'

 

class Example {
   static void main(String[] args) {
       //定义map
      def map = ['key1': "测试",'key2': true];
      //访问map中的值
      println map.key2;
      println map['key1'];
      //定义新的map的key
      map.anotherkey = "another";
      println map.anotherkey;
   }
}

看下输出

$ groovy map.groovy 
true
测试
another

如果不加''的话,map的key和变量同名怎么办呢?看下面例子,也就是说直接使用aa就是简单的字符串,如果需要取变量的值需要用(aa)的方式取

      def aa  ="测试";
      def map1 = [aa:"11",(aa):22];
      println map1.aa;
      println map1."测试";

输出

11
22

Range类

Range类是对List的一种扩展

class Text{
      static void main(String[] args) {
          //包含12345
          def range  = 1..5;
          println range.from;
          println range.to;

          //包含1234个元素
          def range1 = 1..<5;
           println range1.from;
          println range1.to;
   }
}

查看Groovy API技巧

真正写代码的是时候还是要查看 Groovy SDK API的,因为我们不可能记住全部的用法吧

Groovy API文档的地址 www.groovy-lang.org/api.html

我们以上面的Range为例,首先需要定位到Range类,

在API文档查找你需要的使用的函数和属性就行,但是我们发现,这个文档里面并没有,上面使用的from和to属性的介绍,这是为什么?

我们发现索然没有 from/to属性的介绍,但是却有 getFrom 和 getTo 这俩个函数

原来根据Groovy的规则,假如文件有个 xx 的成员变量,Groovy会自动为他添加 getXx()和setXx()俩个函数,所以当我们看到Range中有getFrom和getTo的时候,我们就可以确定Range中有from和to属性

 

闭包

什么是闭包

闭包英语是Cloure,是Groovy中非常重要的数据类型,他代表了一段可执行代码,如下:

class Example {
   static void main(String[] args) {
      def closure = {//闭包是一段代码,所以用花括号圈起来
          String param1,int param2->//箭头前面是参数,后面是代码
          println param1+param2;//这个是代码
        
          //最后一句是返回值,也可以用return,和Groovy函数用法一样
          '返回值'
      }

      def aa =  closure("测试",11);
      closure.call("你好",22);

      println aa;
   }
}


看下输出 :

测试11
你好22
返回值

也就是说,闭包的格式是

def xxx = {paramters -> code} //或者
def xxx = {无参数,纯 code} 这种 case 不需要->符号

调用闭包

闭包对象.call(参数) 
或者: 闭包对象(参数)

这就是闭包的定义和使用,还需要注意一点

如果闭包没有定义参数的话,那就会有一个隐藏参数,这个参数的名字是it,和this的作用相似,it代表闭包的参数

      def closure1 ={ println "hello,$it"}

      closure1("小明");

      def closure2 ={it->println "hello,$it"}
      closure2("小红")
   }


上面的写法等同于下面写法,看下输出

hello,小明
hello,小红

 但是如果闭包中是这种写法,则表示闭包没有参数

   def closure3 = {-> println "text"}

这种写法就不能传参数了,传了的话就会报错

 

闭包使用需要主要的点

省略圆括号

有些函数最后一个参数是闭包,比如下面:

public static <T> List<T> each(List<T> self, Closure closure)

上面这个函数表示针对List的每一个元素,都会送入闭包做些处理,那该如何使用这个函数呢?

class Example {
   static void main(String[] args) {
       //定义list,可以是任何类型的元素
        def list  = [2,"吃",true]
         list.each{
          println it
      }
   }
} 

上面代码有俩个知识点

  • each函数调用的圆括号省略了,在Groovy中当函数的最后一个参数是闭包的话,就可以省略圆括号,比如:
 static def text(int a ,String b,Closure closure){
     def aa  = a + b;
     closure(aa);
 }

调用text方法

      text(1,'第一种调用方式',{
          println it;
      })

      text 2,'第二种调用方式',{
          println it;
      }
      //第三种调用方式,如果最后一个参数是闭包,那么可以放在括号外面
      text(3,"第三种调用方式"){
          println it
      }




打印结果

1第一种调用方式
2第二种调用方式
3第三种调用方式

可以看到上面俩种调用方式,他们的差别就是调用少了括号

这种形式在android中还是很常见的,比如:

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
}

比如这些其实就是闭包的写法,dependencies其实就是一个函数

void dependencies(Closure configureClosure);

看到函数,我们只知道要传入一个闭包,但是参数是什么,返回值是什么?

所以说闭包虽然很方便,但是它和使用它的上下文关联性很强,那么我们到底该怎么确定他的参数和返回值呢? 解决方式只能去看文档

文档介绍的是遍历List,将每个元素传递给给定的闭包。所以这个闭包只有一个参数,就是遍历的元素

所以闭包的使用还是很坑的,很大程度依赖你对API的熟悉程度,所以在最开始,对API的查询是少不了的

闭包内置对象

闭包内部内置了三个对象,this,owner,delegate,我们可直接访问this,owner,delegate调用,或者调用他们的get方法

  • getThisObject() 等于 this
  • getOwner() 等于 owner
  • getDelegate() 等于delegate

那么这三个对象分别指的是那个对象呢?

  • this:指的是定义的闭包的那个类,指向只能是类
  • owner:指向定义闭包的类或者闭包,指向可以是类或者闭包
  • delegate:默认和owner一致,但是可以自己设置(重点)

看下面例子

class OuterClass {
   class InnerClass {
       def outerClosure = {
           def innerClosure = {
           }
           printfMsg("innerClosure", innerClosure)
           println("------")
           printfMsg("outerClosure", outerClosure)
       }
       void printfMsg(String flag, Closure closure) {
           def thisObject = closure.getThisObject()
           def ownerObject = closure.getOwner()
           def delegate = closure.getDelegate()
           println("${flag} this: ${thisObject.toString()}")
           println("${flag} owner: ${ownerObject.toString()}")
           println("${flag} delegate: ${delegate.toString()}")
       }
   }

   def callInnerMethod() {
       def innerClass = new InnerClass()
       innerClass.outerClosure.call()
       println("------")
       println("outerClosure toString ${innerClass.outerClosure.toString()}")
   }

   static void main(String[] args) {
       new OuterClass().callInnerMethod()
   }
}

然后我们看下输出,就可以看出各个对象之间的差距了

innerClosure this: OuterClass$InnerClass@7e4204e2
innerClosure owner: OuterClass$InnerClass$_closure1@29a0cdb
innerClosure delegate: OuterClass$InnerClass$_closure1@29a0cdb
------
outerClosure this: OuterClass$InnerClass@7e4204e2
outerClosure owner: OuterClass$InnerClass@7e4204e2
outerClosure delegate: OuterClass$InnerClass@7e4204e2
------
outerClosure toString OuterClass$InnerClass$_closure1@29a0cdb

闭包中的delegate(委托)

上面说delegate是可以自己设置指向的,就是说可以让闭包通过delegate和某个对象进行关联,下面看下他的具体应用

先定义一个对象

class Person{
    String name
    int age

    Person(String name,int age){
        this.name=name
        this.age=age
        println '初始化'
    }

    void name(String name){
        this.name=name;
    }

    void age(int age){
        this.age=age
    }

    void eat(String foot){
        println "不喜欢吃$foot"
        }

        String toString(){
             "${name}的年龄为${age}"
            }
}
class Main{

    //定义闭包
    def aa ={
        //在闭包中直接调用关联对象Person的方法
        name '小红'
        age 22
        eat '香蕉'
    }

    static void main(String... args){
        def main = new Main()

        Person person = new Person('小明',11);
        person.eat('苹果')
        println person.toString();

        println '-----------------------'
    
        //把闭包和Person对象通过delegate进行关联
        main.aa.delegate=person
        main.aa.setResolveStrategy(Closure.OWNER_FIRST)
        main.aa.call();
        println person.toString();
    }
}

看下输出

初始化
不喜欢吃苹果
小明的年龄为11
-----------------------
不喜欢吃香蕉
小红的年龄为22

那如果关联的对象和闭包所在的类,有名称相同的方法,那么会调用那个方法呢?他会根据下面策略调用

  • Closure.OWNER_FIRST是默认策略。优先在owner寻找,owner没有再delegate

  • Closure.DELEGATE_FIRST:优先在delegate寻找,delegate没有再owner

  • Closure.OWNER_ONLY:只在owner中寻找

  • Closure.DELEGATE_ONLY:只在delegate中寻找 

脚本类

Groovy可以向java一样,引入其他包的类,比如我在文件夹 com.aa中创建文件Aa.groovy

package com.aa
class Aa{
    String a;
    String b;

    Aa(String a,String b){
        this.a=a;
        this.b=b;
    }

    def print(){
        println a+b;
    }
}

这个类其实和java的类就比较相似,如果没有加任何权限修饰符(public,privite)那么Groovy中类和变量都是默认就是public修饰符

然后我们在根目录创建另一个文件Bb.groovy

import com.aa.Aa;

Aa aa = new Aa("小明","小红");
aa.print();

Bb文件先 import com.aa.Aa;然后调用Aa中的print方法

看下执行Bb之后的结果

$ groovy Bb.groovy 
小明小红

脚本到底是什么

在java中,一个类必须要有(class,interface或者其他),不可以不写,但是Groovy可以像写脚本一样,可以直接把想要的事情写入xxx.groovy文件中,而且可以使用 groovy xxx.groovy 来直接执行文件

IO操作

Groovy中的IO操作比java中的简单一些,Groovy的IO操作其实是在Java IO操作上进行了更为简便的封装,并且使用Closure来简化代码,一下是文档地址 

java.io.File: http://docs.groovy-lang.org/latest/html/groovy-jdk/java/io/File.html 
java.io.InputStream: http://docs.groovy-lang.org/latest/html/groovy-jdk/java/io/InputStream.html 
java.io.OutputStream: http://docs.groovy-lang.org/latest/html/groovy-jdk/java/io/OutputStream.html 
java.io.Reader: http://docs.groovy-lang.org/latest/html/groovy-jdk/java/io/Reader.html 
java.io.Writer: http://docs.groovy-lang.org/latest/html/groovy-jdk/java/io/Writer.html 
java.nio.file.Path: http://docs.groovy-lang.org/latest/html/groovy-jdk/java/nio/file/Path.html

读文件

File

直接查看API文档 java.io.File: http://docs.groovy-lang.org/latest/html/groovy-jdk/java/io/File.html

文件内容

你好啊
哈哈哈
class Example {
   static void main(String[] args) {
       //创建文件
     def file = new File("text.txt");

    //输出文件的每一行
    file.eachLine{
        String line ->
        println line;
    }

    //文件内容一次性读出,返回类型为 byte[]
    byte[] aa = file.getBytes()
    String bb = new String(aa);
    println bb;

   }
}


输出
你好啊
哈哈哈
你好啊
哈哈哈

InputStream

首先查看Api文档http://docs.groovy-lang.org/latest/html/groovyjdk/java/io/InputStream.html

 //获取InputStream流
    def ism = file.newInputStream()
    byte[] cc = ism.getBytes()
    String dd = new String(cc);
    println dd;


    //使用闭包操作InputStream,以后在gradle中都是用这种方式
    file.withInputStream{ ism1 ->
    byte[] ee = ism1.getBytes()
    String ff = new String(ee);
    println ff
    //操作 ism. 不用 close。Groovy 会自动替你 close 
   }

输出
你好啊
哈哈哈
你好啊
哈哈哈

写文件

继续看文档 http://docs.groovy-lang.org/latest/html/groovy-jdk/java/io/OutputStream.html

 def srcFile = new File("text.txt")

        def targetFile = new File("copy.txt") 

        targetFile.withOutputStream{ os->

        srcFile.withInputStream{ ins->

        //利用 OutputStream 的<<操作符重载,完成从 inputstream 到 OutputStream //的输出
        os << ins 
			} 
		} 

XML操作

Groovy中操作XML也是非常的简洁,首先提供一个xml文件

<response version-api="2.0">
<value>
<books>

<book available="20" id="1">
<title>Don Xijote</title>
<author id="1">Manuel De Cervantes</author>
</book>

<book available="14" id="2">
<title>Catcher in the Rye</title> <author id="2">JD Salinger</author>
</book>

<book available="13" id="3">
<title>Alice in Wonderland</title>
<author id="3">Lewis Carroll</author> 
</book>

<book available="5" id="4">
<title>Don Xijote</title>
<author id="4">Manuel De Cervantes</author>
</book> 

</books>
</value> 
</response>

开始写代码

 class Example {
   static void main(String[] args) {
        def booksData = new XmlParser().parse("text.xml");
        // 访问第一个book元素的available属性
        def ava = booksData.value.books.book[0].@available
        println ava
        //访问第二个book元素的title
        def data = booksData.value.books.book[2].title.text();
        println data
        //访问第一个book元素的id属性
        def id = booksData.value.books.book[0].@id
        println id

        println "---------------------"
        //遍历boos标签打印title
        booksData.value.books.each{books->
         books.each{book ->
            println book.title.text();
         }   
        }
   }
}

输出
20
Alice in Wonderland
1
---------------------
Don Xijote
Catcher in the Rye
Alice in Wonderland
Don Xijote

元对象编程

首先创建一个对象

class Student{

}

可以看到没有定义任何的属性和方法,这样的话外部调用此对象是不可以调用属性和方法的

class StuText{
    static void main(String[] args) {
       Student student =  new Student()
       student.age=18
       println student.age
       def aa= student.name("小明")
       println(aa)
    }
}

外部这样调用是会报错的,因为没有这样的属性和方法,但是我就是想要这样调用怎么办?

可以用下面的方法

Public interface GroovyInterceptable { 
   Public object invokeMethod(String methodName, Object args) 
   Public object getproperty(String propertyName) 
   Public object setProperty(String propertyName, Object newValue) 
   Public MetaClass getMetaClass() 
   Public void setMetaClass(MetaClass metaClass) 
}

只需要实现GroovyInterceptable这个接口就行

class Student implements GroovyInterceptable{
  protected dynamicProps=[:]
	
   void setProperty(String pName,val) {
      dynamicProps[pName] = val
   }
   
   def getProperty(String pName) {
      dynamicProps[pName]
   } 

    def invokeMethod(String name, Object args) {
      return "called invokeMethod $name $args"
   }
}

如上面代码,再次调用,看下输出

18
called invokeMethod name [小明]

这样的话虽然没有定义方法和属性,但是依然还可以调到,并不会报错

 

methodMissing

上面的代码还有个问题,如果对象定义了name方法,我们依然调不到,因为invokeMethod会把所有方法拦截

class Student implements GroovyInterceptable{
  protected dynamicProps=[:]
	
    def name(def message){
        return message
    }
   void setProperty(String pName,val) {
      dynamicProps[pName] = val
   }
   
   def getProperty(String pName) {
      dynamicProps[pName]
   } 

    def invokeMethod(String name, Object args) {
      return "called invokeMethod $name $args"
   }
}

可以看到上面我们定义了name方法,调用

class StuText{
    static void main(String[] args) {
       Student student =  new Student()
       student.age=18
       println student.age
       def aa= student.name("小明")
       println(aa)
    }
}

输出

18
called invokeMethod name [小明]

最终没有调用到我们想要调用的方法

这样肯定不行,辛亏Groovy中支持methodMissing的概念,与invokeMethod不同的地方他只在调用方法报错的情况下才会被调用

class Student implements GroovyInterceptable{
  protected dynamicProps=[:]
	
    def name(def message){
        return message
    }
   void setProperty(String pName,val) {
      dynamicProps[pName] = val
   }
   
   def getProperty(String pName) {
      dynamicProps[pName]
   } 

    def methodMissing(String name, def args) {         
      println "Missing method"
   } 
}

调用name方法,输出

18
小明

调用到了正确的方法

调用其他没有定义的方法

18
Missing method

就会回调到methodMissing的方法中

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值