Introduction of Groovy

Groovy is a dynamic JVM-based language that has a similar syntax to Java and will eventually compile.class to run on the JVM.

Groovy is fully Java compliant and adds dynamic typing and flexible features, such as support for closures, support for DSLS, and a very flexible dynamic scripting language.

The purpose of this article is to understand the code in Gradle scripts and know how to write them. So I won’t go into Groovy.

Each build script configuration file is a Groovy script file. You can write any Groovy syntactic code inside. Such as defining classes, methods, variables, etc. And since Groovy is fully Java compatible, you can write any Java code, which is fully compatible.

DSL

DSL (Domain Specific Language) in Chinese means Domain Specific Language, which is dedicated to a certain field, but not the whole. So it’s domain-specific.

Gradle scripts are Groovy-based DSLS that specifically address automated builds. We only need to configure Gradle scripts according to the corresponding syntax to achieve the purpose of automatic build, which is also the original intention of DSL.

annotation

Single-line comments

// def name ="Buddha code"
Copy the code

Multiline comment

/* here is a multi-line comment la la la la */Copy the code

Doc comments

/** * here is doc comment * la la la la */Copy the code

The data type

Basic data types in Java, objects that it supports; There are also closure reinforced List, Map collection reinforced File, Stream and other IO types

Types can be declared either explicitly or in def, and those declared in DEF will do type inference in Groovy.

The basic data types are consistent with Java, so I won’t take them out. Objects, strings, closures, etc.;

Also: The semicolon in Groovy can be omitted;

string

You can define a string constant using both single and double quotation marks.

The difference is that single quotes are simply strings and cannot use expressions, operations, evaluations, regex, etc.

task character() {doLast{
      def name = 'Joe'
      def address ="Beijing"
      def age = 19
      println "Single quote double quote is the string name is${name}; age is $age ; address is ${address}"
      println 'You can't evaluate an expression in single quotes like name is ${name}'}}Copy the code

Perform character

gradle character
Copy the code

The result is as follows

Single quotation marks Double quotation marks are the string name is triple; age is 19 ; An expression such as name is cannot be calculated in single quotation marks${name}
Copy the code

Double-quoted strings can be evaluated directly as expressions with a dollar sign followed by curly braces:age

A collection of

The collection is of type java.util.arrayList by default

Def nums = [1,2,4,5,6] println"nums is ${nums.getClass().getName()} size = ${nums.size()}"
Copy the code

The output is

nums is java.util.ArrayList size = 5
Copy the code

You can also explicitly specify the collection type using the AS keyword;

def nums1 = [0,"23", 4,5,62,false] as LinkedList
println "nums1 is ${nums1.getClass().getName()}; size =${nums1.size()}"
Copy the code

The output is

nums1 is java.util.LinkedList; size = 6Copy the code

Or specify the type explicitly up front

LinkedList otherLinked = [3, 4, 5]
Copy the code

Access to the elements

Elements are accessed by subscript

println "The third element is${nums1[2]}The first one from the bottom is${nums1[-1]}; First and last:${nums1[0,-1]}"
println "The second to the fourth:${nums1[1..3]}"
Copy the code

The output is:

The third element is 4, and the first one from the bottom isfalse; First and reciprocal first: [0,false[23, 4, 5]Copy the code

Traverse elements

Iterating through the collection using the each method defaults to it

// iterate over nums1.each {print "$it,"
}
Copy the code

The output is:

0, 23, 4, 5, 62, false.Copy the code

Traversal with subscripts: use the eachWithIndex method

numList.eachWithIndex { int value ,int index->
    println "list[$index] = $value"
    }
Copy the code

An array of

The array definition specifies the array type explicitly

String [] strings = ["I"."'"."m"."is"."a"."dog"."."]
   println "\ n array:${strings.getClass().getName()}"
   strings.each{
       print "$it "
   }

   def multi = [5,7,5,8,54,87] as int[]

   println "\n Specifies the type explicitly using as:${multi.getClass().getName()}"
   multi.each{
       print "$it "
   }
Copy the code

The output is

Array: [Ljava lang. String; I'm is a dog. Use as to explicitly specify the type: [I 5 7 5 8 54 87Copy the code

Add elements

Add elements using list.add ()

numList.add(-11)
Copy the code

You can add one using the << operator

numList << 13
Copy the code

Modify the element

numList[0] = 0
Copy the code

You don’t have to worry about subscripting out of bounds; Groovy automatically increments the desired subscripts, setting the middle ones to NULL

Def LinkedList = [0,1,2,3,4,5] def LinkedList = [0,1,2,3,4,5]print "$it "
        }

        println "\n Add an 11 at position 10"

        numList[10] =11

        println "Added:"

        numList.each{
            print "$it "
        }
Copy the code

The output is:

> Task :collect1 0 1 2 3 4 5 Collect1 0 1 2 3 4 5 NULL NULL null null 11 BUILD SUCCESSFULin 0s
Copy the code

Remove elements

Remove elements using list.remove (). The arguments can be subscripts or values

numList.remove 0
numList.remove((Object)10)
Copy the code

Remove the last element using list.removelast ()

numList.removeLast()
Copy the code

Look for the element

Use list.find () to find the first element that matches the criteria

print "\n list.find() finds the first eligible element numlist.find {it%2==0}"
print numList.find { it%2==0}
Copy the code

Use list.findall () to findAll elements that match the criteria

print "\n list.findAll() finds all elements that meet the criteria numlist.findAll {it % 2 ==0}"
print numList.findAll { it % 2 ==0}
Copy the code

Use list.any () to find elements, returning true as long as one element matches

print "\n list.any() returns true numlist. any {it % 2 ==1}"
print numList.any { it % 2 ==1}
Copy the code

Use list.every () to find elements, and return true only if all elements meet the criteria

print "\n list.every() returns true numlist.every {it % 2 == 0}"
print numList.every { it % 2 == 0}
Copy the code

Statistical elements

Count the number of eligible elements: use list.count ()

print numList.count { it % 2 ==0 }
Copy the code

Max: list.max (); min: list.min ()

print "\n maximum is${numList.max()}The minimum is zero${numList.min()}The smallest absolute value is"
print numList.min { Math.abs it}
Copy the code

Map

Maps are defined as key-value pairs, separated by commas

def colors = [red:'#FF0000',green:'#00FF00',blue:'#0000FF']
Copy the code

There are three ways to access elements in a Map:

  • map.key
  • map[key]
  • map.get(key)

Such as:

task map{
    doLast{
        def colors = [red:'#FF0000',green:'#00FF00',blue:'#0000FF']
        println " map calss is ${colors.getClass().getName()}"
        println "Color.red = is accessed via map.key${colors.red}"
        println "Colors ['red'] = is accessed by map[key]${colors['red']}"
        println "Color.get (red) = is accessed through map.get(key)${colors.get('red')}"}}Copy the code

The output is:

> Task :map map calss is java.util.LinkedHashMap color.red =#FF0000Access colors[by map[key]'red'] = #FF0000Color.get (red) = is accessed through map.get(key)#FF0000


BUILD SUCCESSFUL in 0s
1 actionable task: 1 executed
Copy the code

Add elements

// Add the element colors['pink'] = '#FF00FF'
colors.yellow = '#FFFF00'
Copy the code

Modify the element

// Modify the color.red = element'red'
colors['blue'] = 'blue'
println "The modified element is colors.red =${colors.red},colors.blue = ${colors.blue}"
Copy the code

Remove elements

// Remove the element colors.remove('red')
Copy the code

Traverse elements

Use the each method as above

// pass through colors.each{println"${it.key} :${it.value}"
}
Copy the code

Look for the element

The search method is the same as above, except that the parameters are changed to map. Entry or key,value; Just use find as an example:

The find method

def green = colors.find { key ,value ->
  if(key.equals('green')) {
      return colors[key]
  }
  return null
}

println "The result is${green}"


def blue = colors.find { Map.Entry entry ->
    if(entry.key.equals('blue')) {return entry.value
    }
    return null
}
println "The result of the search is${blue}"
Copy the code

methods

Methods are also defined using def

/ def Max (int a,int b){if(a>b){
      return   a
    }else{
      return   b
    }
}
Copy the code

Return can be omitted

Groovy returns the last line of code during execution

/ def Max (int a,int b){if(a>b){
         a
    }else{
       b
    }
}
Copy the code

The parentheses can be omitted;

Parentheses can be omitted when calling a method; Use Spaces to separate parameters

def printMaximum(int a,int b){
    if(a>b){
        println "The maximum value of $a and $b is $a"
    }else{
        println "The maximum value of $a and $b is $b"
    }
}

task method {
    doLast{
        println "max is The ${Max (0, 1)}"
        printMaximum 10, 20}}Copy the code

The output is

> Task :method
max is 1
The maximum value of 10 and 20 is 20

BUILD SUCCESSFUL in 0s
1 actionable task: 1 executed
Copy the code

Blocks of code can be passed as arguments

A code block is a piece of code surrounded by curly braces, which is essentially a closure;

For example each method

The original one would look something like this

colors.each({println it})
Copy the code

After the formatting

colors.each({
    println it
})
Copy the code

Groovy specifies that the last argument is a closure, which can be placed outside a method

colors.each(){
    println it
}
Copy the code

You can omit the parentheses of the method when you call it and that’s it

colors.each {
    println it
}
Copy the code

closure

Closures are an important feature of Groovy and are the foundation of DSLS.

A closure is simply an anonymous block of code.

Closures in Groovy are instances of the groovy.lang.Closure class, which allows closures to be assigned to variables or fields.

Define a closure

def hello = { println "Hello Buddha code" }
Copy the code

Call the closure

hello.call()
Copy the code

Another way to call is to just follow ()

hello()
Copy the code

Let’s simulate the execution of each, specifying a method to iterate over the elements in the collection

/* * closure is the closure argument */ def customEach(closure){// Iterate over the elementfor(int i in1.. Closure (I)}} closure(I){closure(I){closure(I)}}Copy the code

Call this method, passing in a closure to print the element; If the closure has only one argument, the default is IT

// If there is only one argument, the default is it customEach {println it}Copy the code

If the closure accepts more than one parameter, it must list the parameters explicitly, using -> to separate the parameters from the body

Again, simulate an iteration of a map:

def eachMap(closure){
    def map1 = [name:'Buddha code',age:666] map1. Each {closure(it. Key,it. Value)}} ····· Use -> to separate parameters from body eachMap {key,value -> println"$key:$value"
}
Copy the code

Commissioned by the closure

The power of Groovy closures is that they support delegation of closure methods.

Groovy closures have three important properties

  • ThisObject closure definition in the class
  • Owner represents the object or closure in which the closure is defined. (Closures can still be defined within a closure.) This is the nearest principle, as explained below
  • The delegate is the same as the owner by default and can be changed manually.

If you define a closure in a class, all three properties are equal by default;

For example, an ACT closure is defined in the Person class

class Person{
    private String name

    public int getAge(){
       12
    }

    Closure act ={
         println "thisObject:${thisObject.getClass()}"
        println "owner:${owner.getClass()}"
        println "delegate:${delegate.getClass()}"}}Copy the code

Calling the closure gives the following output

> Task :test
thisObject:class Person
owner:class Person
delegate:class Person

BUILD SUCCESSFUL in 0s
1 actionable task: 1 executed
Copy the code

If the closure is defined in a closure, thisOjbect is different from the other two because thisObject is the class that represents the definition closure, and owner represents the class or closure

This time I’m going to make a closure inside a closure and let’s see

class Person{
    private String name

    public int getAge(){
       12
    }

    Closure act ={
        println "thisObject:$thisObject"
        println "owner:$owner"
        println "delegate:$delegate"
    }

    Closure eat = {
        def test = {
            println "thisObject:${thisObject.getClass()}"
            println "owner:${owner.getClass()}"
            println "delegate:${delegate.getClass()}"
        }
        test()}}Copy the code

Executing the EAT closure yields the following result

> Task :test
thisObject:class Person
owner:class Person$_closure2
delegate:class Person$_closure2

BUILD SUCCESSFUL in 0s
1 actionable task: 1 executed
Copy the code

“ThisObject” and “owner” are already different because “thisObject” refers to the class in which the definition is located, while “owner” refers to the class or closure in which the definition is located.

The three properties are pretty clear,

Entrust the strategy

Whenever a property is accessed in a closure or a method is called without an explicit set object, a delegate policy is invoked. This delegate policy determines how to access properties or invoke methods.

There are several policies that can be changed through the closure properties: resolveStrategy

  • The default policy for Closure.OWNER_FIRST is to look for properties and methods in owner and delegate if you can’t find them.
  • Closure.DELEGATE_FIRST is the opposite of the DELEGATE_FIRST, and if not, the owner
  • Closure.OWNER_ONLY is only found on owner, ignoring delegate
  • DELEGATE_ONLY only looks for the ignore owner on the delegate
  • Closure.TO_SELF advanced option, developer defined policy

Let’s demonstrate the policy change in action with a nested class.

Define two classes Person and an inner class Foot, and both have a name attribute. Person has an additional age attribute.

class Person{
    private String name

    public int getAge(){
       12
    }

    class Foot {
      String name
      Closure walk = { it ->
          println "name is $name,age is $age ,delegate is ${delegate.getClass()}"Delegate = it; resolveStrategy = Closure.DELEGATE_FIRST println"Change policy to Closure.DELEGATE_FIRST Delegate first"
          println "name is $name, age is $age ,delegate is ${delegate.getClass()}"

      }
    }

    void walk(){
        Foot foot = new Foot(name:'feet');
        foot.walk(this)
    }
}
Copy the code

Call the Walk method of Person

Person person = new Person()
person.name ="Buddha code"
person.walk()
Copy the code

You get the following output

> Task :testName is the foot,age is 12,delegate is the class Person$FootChange the policy to Closure.DELEGATE_FIRST Delegate Priority name is the Delegate code, age is 12, Delegate is class Person BUILD SUCCESSFULin 0s
1 actionable task: 1 executed
Copy the code

Let me explain the output

The first name is the foot; This is because the default policy is Closure.OWNER_FIRST looks for properties in owner; The owner of course is Foot.

The second name is the Buddhist code; This is because the policy changes to clousre.delegate_first to be looked for on the delegate first, and then changes the delegate property to be the Person instance passed in, whose value is explicitly declared above as a Dharmaphoto encoding.

Age only declares the getAge() method in Person, explicitly returning 12. So even if you change the policy, even if you change the delegate value, it’s still 12.

Note: Of the three properties, only the delegate property can be modified.

Common closure use in Gradle

Define a User class

class User{

    String name
    int age
    
    def dumpUser(){

        println "name is $name,age is $age ."}}Copy the code

Define a method in the build configuration script that passes in a closure parameter to configure the User class

Delegate policy changes to the closure and set the delegate property

def user(Closure<User> closure){
    User user = new User()
    closure.delegate = user
    closure.resolveStrategy = Closure.DELEGATE_FIRST
    closure()
}
Copy the code

This is the case in use. Gradle has many DSL configurations such as the task we created

task configClosure() {doLast{
      user {
        name = 'Buddha code'
        age = 0
        dumpUser()
      }
    }
}
Copy the code

The output is

> Task :configClosure
name is 佛系编码,age is 0 .

BUILD SUCCESSFUL in 0s
1 actionable task: 1 executed
Copy the code

This is the closure API portal

class

Here are only the differences with Java.

Let’s look at some code:

task obj{
    doLast{
        Person p = new Person()
        println "Before assignment:${p.name}"
        p.name = 'Buddha code'
        println "After the assignment:${p.name}"

        println "age is ${p.age}"
    }
}

class Person{
    private String name

    public int getAge() {12}}Copy the code

Execute the output of the obj task

Task :obj Before no assignment: NULL After assignment: Fo code age is 12 BUILD SUCCESSFULin 0s
1 actionable task: 1 executed
Copy the code

There are no get/set methods that define the name attribute in the Person class; You can set and modify its value;

That’s because Groovy took care of the get/set methods.

The age attribute is also not defined in the Person class, but can be used if a getAge() method is defined.

However, because the set method is not defined, the age attribute can only be accessed.

The operator

Only common operators that are different from Java are listed here

Airlift operator

The object itself is used when the object is not empty, and the given value is used when the object is empty. Often used to give a default value for a nullable variable.

task operator {
    doLast{ Person person = new Person(); Def name = person.name? Person.name:'Buddha code'// getAge returns 12 not empty so def age = person.age? :10 println"name is $name , age is $age"}}Copy the code

The output

> Task :operator name is f code, age is 12 BUILD SUCCESSFULin 0s
1 actionable task: 1 executed
Copy the code

Safe navigation operator

A null exception is thrown when a property or method on an object is called if the object is empty. The. Operator does not throw an exception when the object is empty and the expression value is empty.

task operator {
    doLast{
        User user
        println "user.name is ${user? .name}"}}Copy the code

The output is

> Task :operator
user.name is null

BUILD SUCCESSFUL in 0s
1 actionable task: 1 executed
Copy the code

assertions

Assertions are used to verify that an assumed condition is true, and in Groovy assertions, if the assumed condition is not true, then java.lang.AssertionError is thrown.

Groovy assertions are completely different from Java assertions. Groovy assertions are a language feature that has always been turned on and is completely independent of the JVM assertion feature – EA. So it’s our preferred way to do unit testing.

For example,

assert 1==2 :"One does not make two."
Copy the code

The following exception is thrown

···· * What went wrong: Execution failedfor task ':operator'> 1 does not equal 2. Expression: (1 == 2)Copy the code

Of course, it’s ok not to give a message

assert 1==2
Copy the code

So the exception looks like this.

Execution failed for task ':operator'.
> assert 1==2
          |
          false
Copy the code

When using assertions, it is best to give a message that helps others understand and maintain your code and clarify your intentions.

Groovy API query methods

Closure parameters can only be queried in the API, there is no good way.

The Groovy document addresses are listed here so you can query the relevant apis

  • grammar
  • The API documentation

Operating instructions

To use gradle or./gradle or gradlew, you must have gradle installed and environment variables set. This is possible in the directory where Gradle is located.

Build. gradle is the default build script for Gradle. When running gradle commands, build.gradle is found in the current directory by default.

You can also specify the file to load execution with the -b argument.

For example, to perform the operator task in Groovu-basic. build

gradle -b groovy-basic.gradle operator
Copy the code

If you want to execute the above test code, the steps are

  1. Create a build.grale file or create a project using Gradle
  2. Define a task and add an action
task test{
    doLast{// here is the code}}Copy the code
  1. Paste the code
  2. Run the task
gradle test
Copy the code

Attached is my Gradle version

Welcome to attention