An overview of the

I have started a new series on Gradle. The goal of this series is to understand Gradle thoroughly, mainly by taking notes of my own understanding so that I don’t forget

Gradle series 1: Learning Groovy

Configuring the Groovy environment

Since Groovy runs on a Java virtual machine, make sure your computer has a Java environment

  • First go to this address to download Groovy www.groovy-lang.org/download.ht…

  • Then download the compressed package, unzip to the location you need

  • Finally modify the.bash_profile file

Vi ~/. Bash_profile Add the following statement export PATH=$PATH:/Users/ * * * /Documents/groovy-2.412./bin
Copy the code

And then terminal input

source ~/.bash_profile
groovy -v
Copy the code

Seeing the groovy version number indicates that the configuration environment is complete

Then create a hello.groovy file with VscodeCode as follows:

class Example {
   static void main(String[] args) {
        // Use println to print information to stdout
        /* There is no comment information */
        println 'Test data'
        println "Hello World"; }}Copy the code

Run this file

This completes the configuration of the environment

Groovy Syntax Learning

Something to know in advance

  • Groovy annotations are the same as Java/ / or / * * /
  • Groovy statements can be terminated without semicolons, which is actually intended to make code more concise
  • Dynamic typing is supported in GroovyYou can define variables without defining their typesGroovy defines variables that can be useddefKey words,Although def is not required, it is recommended to use it to make the code clearer
def variable1 = 1 // Do not use semicolon endings
def varable2 = "I am a person"
def int x = 1 // When a variable is defined, the type can also be specified directly
Copy the code
  • When a function is defined, the type of the argument may not be specified
String testFunction(arg1,arg2){// Do not specify the parameter type...
}
Copy the code
  • In addition to defining variables without specifying a type, the return value of a Groovy function can also be untyped
// For untyped function definitions, the def keyword must be used
def nonReturnTypeFunc(){
last_line The result of the last line of code is the return value of this function
}

// If the function return type is specified, the def keyword is not needed to define the function
String getString(){
return "I am a string" 
}
Copy the code
  • Function return value: In Groovy functions, you can do without itruturnIf return is not used, the last line of code executed in the function is set to the return value
// The following function returns the string "getSomething return value"
def getSomething(){
"getSomething return value" // If this is the last line, the return type is String
1000 // If this is the last line of code, the return type is Integer
}
Copy the code

If the function specifies the type of the return value, then the correct type must be returned or an error will be reported. Arbitrary data types can be returned only if the return value is dynamically typed

  • Groovy’s support for strings is powerful
1Single quotes' 'The $symbol is not escapeddef singleQuote='I am $ dolloar' // Output is I am $dolloar

2Double quotation marks""If a character has a $sign in it, it evaluates the $expression first.def doubleQuoteWithoutDollar = "I am one dollar" // Output I am one dollar
def x = 1
def doubleQuoteWithDollar = "I am $x dolloar" // I am 1 dolloar

3Three quotes'''xxx'''String support arbitrary line breaks for exampledef multieLines = ''' begin line 1 line 2 end '''
Copy the code
  • Finally, in addition to no extra credit for each line of code, Groovy allows function calls to be made without parentheses
println("test") ---> println "test"
Copy the code

Note: Although it is possible to write code without parentheses on function calls, it is possible to write code without parentheses on function calls that are Groovy apis, such as the print method. Otherwise, parentheses are required because Groovy has some problems with this support

Data types in Groovy

Here we will only cover things that are not quite the same as in Java

Basic data types

As a dynamic language, everything in Groovy is an object, so the basic data types like int and Boolean in Java actually correspond to their wrapper data types like int-INTEGER and Boolean

Container classes

There are really only three types of containers in Groovy

  • List: Linked List, the underlying corresponding to the List interface in Java, generally using ArrayList as the implementation class
  • Map: Key-value table, whose underlying counterpart is the LinkedHashMap in Java
  • -Leonard: It’s actually an extension of the List

List

class Example {
   static void main(String[] args) {
       // Define a list, which can be any type of element
        def list  = [2."Eat".true]
        // List element assignment, do not worry about the array out of bounds, if exceeded the length of the list will automatically populate the index
        list[4] ="tt"
        / / traverse the list
        for (a inlist) { println a; }}}Copy the code

Map

Key must be a string. Value can be any object. Key can be added with “” or not, for example, def Map = [key1:” test “,key2: true]. So the system will automatically process the string ‘key’,’key’

class Example {
   static void main(String[] args) {
       / / define the map
      def map = ['key1': "Test".'key2': true];
      // Access the values in map
      println map.key2;
      println map['key1'];
      // Define a new map key
      map.anotherkey = "another"; println map.anotherkey; }}Copy the code

Look at the output

$ groovy map.groovy 
trueAnother testCopy the code

What if the map key has the same name as the variable without ”? If you want to fetch the value of a variable, you need to fetch it as (aa)

      def aa  ="Test";
      def map1 = [aa:"11",(aa):22];
      println map1.aa;
      println map1."Test";
Copy the code

The output

11
22
Copy the code

The Range class

The Range class is an extension of List

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

          // Contains 1234 elements
          def range1 = 1.. <5; println range1.from; println range1.to; }}Copy the code

Check out Groovy API tips

The time to actually write code is still to look at the Groovy SDK API, since we won’t be able to remember all the usage

The address of the Groovy API documentation is www.groovy-lang.org/api.html

Using Range above as an example, we first need to locate the Range class

Then look in the API documentation for the functions and attributes you need to use, but we find that there is no description of the from and to attributes used above. Why is this?

There is no introduction to the FROM /to attribute, but there are getFrom and getTo functions

Groovy automatically adds getXx() and setXx() to a file if it has a member variable of xx, so when we see getFrom and getTo in the Range, we can be sure there are from and to attributes in the Range

closure

What is a closure

Closure English is Cloure, a very important data type in Groovy, and it represents a piece of executable code like this:

class Example {
   static void main(String[] args) {
      def closure = {// The closure is a piece of code, so it is enclosed in curly braces
          String param1,int param2->// Arrows are preceded by parameters and followed by code
          println param1+param2;// This is the code
        
          // The last sentence is a return value. You can also use return, just like Groovy
          'Return value'
      }

      def aa =  closure("Test".11);
      closure.call("Hello".22); println aa; }}Copy the code

Look at the output

test11hello22The return valueCopy the code

In other words, the closure is of the format

def xxx = {paramters -> code} / / or
defXXX = {no arguments, pure code} of this kindcaseThe -> symbol is not requiredCopy the code

Call the closure

Closure object.call (parameter) or: closure object (parameter)Copy the code

This is how closures are defined and used, but one more thing to note

If the closure does not define a parameter, there is a hidden parameter named it, which is similar to this in that it represents the closure parameter

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

      closure1("Xiao Ming");

      def closure2 ={it->println "hello,$it"}
      closure2("Little red")}Copy the code

So this is the same thing as this, so let’s look at the output

Hello, Xiao Ming. Hello, Xiao HongCopy the code

But if the closure is written this way, then the closure has no arguments

      def closure3 = {-> println "text"}
Copy the code

This way you can’t pass parameters, and if you do, you’ll get an error

Closure use requires major points

Omit parentheses

Some functions have a closure as the last argument, such as the following:

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

So how do we use this function that says that for each element of a List, we’re going to do something in a closure?

class Example {
   static void main(String[] args) {
       // Define a list, which can be any type of element
        def list  = [2."Eat".true]
         list.each{
          println it
      }
   }
}
Copy the code

There are two things about the code above

  • The parentheses for each call are omitted. In Groovy, the parentheses are omitted when the last argument to a function is a closure, as in:
 static def text(int a ,String b,Closure closure){
     def aa  = a + b;
     closure(aa);
 }
Copy the code

Calling the Text method

      text(1.'First call',{
          println it;
      })

      text 2.'Second call',{
          println it;
      }
      // If the last argument is a closure, it can be placed outside the parentheses
      text(3."Third way to call"){
          println it
      }
Copy the code

Print the result

1The first way to call2Second way to call3Third way to callCopy the code

As you can see, the difference between these two calls is the absence of parentheses

This form is common in Android, for example:

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

For example, these are closures, and dependencies are a function

void dependencies(Closure configureClosure);
Copy the code

While this method is easy to write, it can be confusing to read at first. Take some time to get used to it

How do I determine the parameters of a closure

Another thing that gets a little confusing is how to determine the parameters of a closure, like the each method

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

When we look at the function, we just know we’re passing in a closure, but what are the arguments, what are the return values?

So while closures are convenient, they are very relevant to the context in which they are used. So how do we determine their arguments and return values? The solution is to look at documentation

The documentation describes walking through the List, passing each element to a given closure. So the closure has only one argument, which is the iterated element

Closures can be tricky to use, depending heavily on how familiar you are with the API, so it’s important to query the API in the beginning

Closure built-in objects

The closure has three built-in objects, this, Owner, and Delegate, and we can directly access the this, Owner, and Delegate calls, or call their GET methods

  • GetThisObject () is equal to this
  • GetOwner () is equal to the owner
  • GetDelegate () is equal to the delegate

So which objects do these three objects refer to?

  • This: refers to the class of the defined closure and can only refer to a class
  • Owner: Points to the class or closure that defines the closure. The point can be a class or closure
  • Delegate: Defaults to owner, but can be set yourself (important)

Look at the following example

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()
   }
}
Copy the code

Then we look at the output and see the difference between the objects

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
Copy the code

A delegate in a closure

A closure can be associated with an object via a delegate. Let’s see how it works

Let’s define an object

class Person{
    String name
    int age

    Person(String name,int age){
        this.name=name
        this.age=age
        println 'initialize'
    }

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

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

    void eat(String foot){
        println "Don't like to eat $foot"
        }

        String toString(){
             ${name} = ${age}}}Copy the code
class Main{

// Define closure
def aa ={
    Call the method on the associated object Person directly in the closure
    name 'little red'
    age 22
    eat 'banana'
}

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

    Person person = new Person('Ming'.11);
    person.eat('apple')
    println person.toString();

    println '-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --'
    
    // Associate the closure with the Person object via a delegatemain.aa.delegate=person main.aa.setResolveStrategy(Closure.OWNER_FIRST) main.aa.call(); println person.toString(); }}Copy the code

Look at the output

Initialization does not like to eat apples xiao Ming's age is11----------------------- does not like to eat bananas xiao Hong's age is22
Copy the code

If the associated object and the closure’s class have a method with the same name, which method will be called? He will call according to the following policy

  • Closure.OWNER_FIRST is the default policy. The delegate of owner is not specified

  • Closure.DELEGATE_FIRST: Finds the delegate first, the delegate does not return the owner

  • Closure.OWNER_ONLY: Search only in owner

  • Closure.DELEGATE_ONLY: Look only in the delegate

The script class

Groovy can import classes from other packages as Java does, for example I created the file aa.groovy in the folder com.aa

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

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

    defprint(){ println a+b; }}Copy the code

This class is similar to Java classes in that all classes and variables in Groovy are public by default if you don’t add any permissions (public, privite)

Then we create another file bb.groovy in the root directory

import com.aa.Aa;

Aa aa = new Aa("Xiao Ming"."Little red");
aa.print();
Copy the code

Bb file import com.aa.aa; Then call the print method in Aa

Let’s see what happens after Bb

$groovy BbCopy the code

What exactly is a script

In Java, a class must have (class, interface, etc.) and cannot be written out, but Groovy can write desired things directly to XXX. Groovy files, and you can use Groovy xxx. Groovy to execute files directly

IO operations

I/O operations in Groovy are simpler than in Java. Groovy’s IO operations are simply wrapped around Java IO operations and use Closure to simplify the code

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
Copy the code

Read the file

File

Directly to the API documentation Java. IO. File: http://docs.groovy-lang.org/latest/html/groovy-jdk/java/io/File.html

The file content

Hello ha ha haCopy the code
class Example {
   static void main(String[] args) {
       // Create a file
     def file = new File("text.txt");

    // Prints each line of the file
    file.eachLine{
        String line ->
        println line;
    }

    // File contents read once, return type byte[]
    byte[] aa = file.getBytes()
    String bb = newString(aa); println bb; }}Copy the code

The output

Hello, ha, ha, haCopy the code

InputStream

First of all check the Api documentation at http://docs.groovy-lang.org/latest/html/groovyjdk/java/io/InputStream.html

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


    // InputStream is operated with closures. This is the way we will operate in gradle
    file.withInputStream{ ism1 ->
    byte[] ee = ism1.getBytes()
    String ff = new String(ee);
    println ff
    // Run the ism. Do not use close. Groovy will automatically close for you
   }
Copy the code

The output

Hello, ha, ha, haCopy the code

Write files

Continue to look at the document at 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->

        Output from inputStream to OutputStream // using the << operator overload of OutputStream
        os << ins 
			} 
		} 
Copy the code

XML operation

Manipulating XML in Groovy is also pretty neat, starting with an XML file

<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>
Copy the code

Start coding

class Example {
   static void main(String[] args) {
        def booksData = new XmlParser().parse("text.xml");
        // Access the available attribute of the first book element
        def ava = booksData.value.books.book[0].@available
        println ava
        // Access the title of the second book element
        def data = booksData.value.books.book[2].title.text();
        println data
        // Access the id attribute of the first book element
        def id = booksData.value.books.book[0].@id
        println id

        println "-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -"
        // Prints title by traversing booS labelsbooksData.value.books.each{books-> books.each{book -> println book.title.text(); }}}}Copy the code

The output

20
Alice in Wonderland
1
---------------------
Don Xijote
Catcher in the Rye
Alice in Wonderland
Don Xijote
Copy the code

Metaobject programming

Start by creating an object

class Student{

}
Copy the code

You can see that no properties or methods are defined, so external calls to this object cannot call properties or methods

class StuText{
    static void main(String[] args) {
       Student student =  new Student()
       student.age=18
       println student.age
       def aa= student.name("Xiao Ming")
       println(aa)
    }
}
Copy the code

External calls like this will get an error because there are no such properties and methods, but what if I just want to call like this?

This can be done in the following way

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) 
}
Copy the code

All you need to do is implement the GroovyInterceptable interface

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"}}Copy the code

Call it again as above and see the output

18Called invokeMethod name [xiao Ming]Copy the code

In this way, methods and attributes are not defined, but can still be called, and no errors will be reported

methodMissing

Another problem with the above code is that if the object defines a name method, we still can’t call it because invokeMethod intercepts all methods

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"}}Copy the code

As you can see above we defined the name method called

class StuText{
    static void main(String[] args) {
       Student student =  new Student()
       student.age=18
       println student.age
       def aa= student.name("Xiao Ming")
       println(aa)
    }
}
Copy the code

The output

18Called invokeMethod name [xiao Ming]Copy the code

We didn’t end up calling the method we wanted

Groovy supports the concept of methodMissing, which unlike invokeMethod will only be called if an error occurs when a method is called

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"}}Copy the code

Call the name method, output

18Xiao MingCopy the code

The correct method is called

Call other undefined methods

18
Missing method
Copy the code

It’s going to call back to the methodMissing

reference

In-depth understanding of Android Gradle