Groovy基本语法(一)

  • 注释

    Groovy的注释和Java一样,//代表单行注释,/**/代表多行

  • 行末不用加分号

  • 允许省略权限修饰符,默认为public

    1
    2
    3
    4
    class Person{
    String name
    int age
    }
  • 字符串

    • 单引号字符串

      等同于Java中的字符串

      1
      name = 'jack'
    • 双引号字符串

      可以将变量直接插入到字符串当中,这种叫做内插字符串。编译器会把美元和花括号中的内容替换成实际的值,内插字符串中还可以进行表达式计算。

      1
      2
      3
      4
      5
      6
      name = 'jack'
      a = "hell ${name}"
      //输出 hell jack
      println a
      //hello jack23
      println("hello ${name + "23"}")

      当内插字符串可以由前后的符号区分出的时候,花括号可以省略

      1
      2
      3
      def person = [name: 'jack', age: 15]
      //jack 的年龄是15
      println("$person.name 的年龄是$person.age")

      注意:内插字符串的类型不是String,是GString类型。普通的Java字符串是不变的,而GString是可变的。另外它们的哈希值也不同。因此在使用Map等数据类型的时候需要格外注意,避免使用GString作为Map的键。

      1
      2
      3
      4
      5
      def person = [name: 'jack', age: 15]
      a = "$person.name is"
      //class org.codehaus.groovy.runtime.GStringImpl
      //GStringImpl继承GString
      println(a.class)
    • 多行字符串

      用三引号’’’ ‘’’表示,会保留字符串中的空格换行

      1
      2
      words = '''白日依山尽,
      黄河入海流'''
  • 基本类型

    Groovy其实就是Java的拓展,所以Java的基本类型Groovy都支持。使用起来和Java相似,但由于在Groovy当中,一切皆对象,所以声明的基本类型变量自动使用其包装类型。

    1
    2
    int age = 13
    flag = true

    这里看似age是int类型,flag是boolean类型,但其实在Groovy当中,一切皆对象。我们打印下age和flag的类型就知道了

    1
    2
    3
    4
    //class java.lang.Integer
    println(age.class)
    //class java.lang.Boolea
    println(flag.class)

    另外,在Groovy如果需要声明字符类型,需要使用下面的方法进行转换

    1
    2
    3
    def gender = '女' as char
    //class java.lang.Character
    println(gender.class)

    Groovy中的布尔类型要比Java的布尔类型语义要丰富,未到结尾的迭代器、非空对象引用、非零数字都认为是真;空集合、空字符串等认为是假。详情参见Groovy文档 core-semantics#Groovy-Truth

  • 变量

    1. 使用def关键字来定义变量,但def并不是必须的

    2. 变量类型不用声明,会由编译器自动类型推断,无法推断时就是Objectl类型

      1
      2
      3
      4
      5
      //类型可加可不加
      def age = 13;
      def String name = "jack"
      //def也可以省略
      address = "china"
    3. 变量支持动态类型

      1
      2
      3
      4
      gender = "女"
      gender = 13
      //13
      println(gender)
    4. 可以同时声明多个变量并对其赋值

      1
      2
      3
      4
      5
      6
      7
      def (a, b, c) = [1, true, 'name']
      //1
      println(a)
      //true
      println(b)
      //name
      println(c)

      如果左边的变量数比右面的值多,那么剩余的变量就是null

      1
      2
      3
      4
      5
      6
      7
      def (a, b, c) = [2, true]
      //2
      println(a)
      //true
      println(b)
      //null
      println(c)

      如果等号右面比左面多,那么多余的值会被忽略。

      1
      2
      def (a, b) = [1, false, 'dsf']
      assert a == 1 && b == false
  • 方法

    1. 同变量一样,用def来声明一个方法,如果指明了返回值,def可省略

      1
      2
      3
      4
      5
      6
      def String getName(name){
      return name
      }
      String getName1(name) {
      name
      }
    2. 方法的返回值类似可省略,参数的类型也不用指定,Groovy会自行推断。如果指定了类型,调用方法时参数必须是指定的类型。如果在方法体中没有明确return,会将最后一行的值return

      1
      2
      3
      4
      5
      def getAge(age) {
      age
      }
      //结果为13
      println(getAge(13))
    3. 方法调用时,可以不加括号

      1
      2
      3
      4
      5
      def printName(name) {
      println(name)
      }
      //打印13
      printName 13

      但这样有时会引起歧义,如

      1
      2
      3
      4
      5
      def doSomething(){
      println('haha')
      }
      //报错
      doSomething

      因为Groovy无法分清此时是要调用方法,还是定义了一个变量,因此,推荐一些常见的系统内置方法省略括号,其它的还是带上括号比较好。

    4. 默认参数

      Groovy支持方法的参数设置默认值,这样在方法的调用时,可以省略参数,使用预定义的默认值

      1
      2
      3
      4
      5
      6
      7
      8
      9
      def repeatName(name, count = 3) {
      for (i = 0; i < count; i++) {
      println(name)
      }
      }
      //打印3次
      //repeatName("jack")
      //打印1次
      repeatName("jack",1)
  • 表达式与运算符

    1. Groovy运算符和Java基本类似,只不过数学运算符多了一个幂运算**和乘方赋值**=

      1
      2
      3
      4
      5
      //幂运算 2的三次方 打印8
      println(2**3)
      a = 4
      //a此时为4的3次方 27
      a **= 3
    2. Groovy中的一些特殊运算符

      • 展开运算符

        一个集合使用展开运算符(*.)可以得到一个元素为原集合各个元素执行后面指定方法所得值的集合

        展开运算符是空值安全的,如果遇到了null值,不会抛出空指针异常,而是返回空值。

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        class Student{
        String name

        @Override
        String toString() {
        return name
        }
        }
        def s = [new Student(name: 'jack'),new Student(name: "12")]
        //[jack, 12] 这里调用了集合每个元素的toString方法 得到结果后,又组成一个新的集合
        println s *.toString()
      • 可空运算符

        由于Groovy中“非空即真”,所以原java中的三元运算符可以简化为二元运算符,常用来给某个可空变量赋默认值

        1
        2
        3
        4
        5
        a = 3
        // b为2 java写法
        b = a > 3 ? 4 : 2
        //groovy简化
        c = a > 3 ?: 2
      • 安全占位符

        安全占位符(?.)主要用于避免空指针异常

        1
        2
        3
        4
        5
        6
        7
        8
        9
        Student s
        //抛NullPointerException
        println("s的名字是${s.name}")
        //为避免空指针,java写法
        if (s != null) {
        println("s的名字是${s.name}")
        }
        //安全占位符 输出s的名字是null
        println("s的名字是${s?.name}")
      • 字段访问运算符

        在Groovy中默认使用.运算符引用属性的Getter和Setter。但如果希望直接访问某个字段,可以使用字段访问运算符.@

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        class Student {
        String name

        public Student(String name) {
        this.name = name
        }

        String getName() {
        println("getName: " + name)
        return name
        }
        }
        def s = new Student('jack')
        //打印 getName: jack
        //jack
        println(s.name)、
        //直接访问字段 打印jack
        println(s.@name)
      • 方法指针运算符

        Groovy的.&方法指针运算符允许将一个方法赋给变量,然后我们就可以像调用方法那样使用变量。方法引用的实际类型是Groovy的闭包Closure。这种运算符可以将方法当成一个闭包作为另一个方法的参数

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        //定义一个集合
        def names = ['jack', 'mick', 'marry']
        //常规遍历
        names.each {
        println(it)
        }
        //定义一个方法
        def printItem(item) {
        println(item)
        }
        //使用方法指针运算符将printItem方法赋值给a
        def a = this.&printItem
        //将a作为each方法的参数
        names.each a
      • 范围运算符

        ..用来创建范围Range。范围是列表中的一种,它继承了List接口。默认情况下是闭区间的

        1
        2
        3
        4
        5
        def nums = 1..4
        //1 2 3 4
        nums.each {
        println(it)
        }
      • 比较运算符

        <=>运算符相当于调用compareTo方法。Java 语言的 compareTo() 方法返回负整数、0 或正整数,代表对象小于、等于或大于指定对象

        1
        2
        //打印-1
        println(1 <=> 3)
      • 成员运算符

        in相当于调用contains或者isCase方法

        1
        2
        3
        def nums = [3, 32, -2]
        //true
        println(3 in nums)
      • 相等运算符

        ==运算符和Java中的不同。在Groovy中它相当于调用equals方法。如果需要比较引用,使用is

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        class Test{
        @Override
        boolean equals(Object obj) {
        return true
        }
        }
        a = new Test()
        b = new Test()
        //打印true
        println(a == b)
        //false 比较是否为同一个引用
        println(a.is(b))
      • 转换运算符

        我们可以使用Java形式的(String) i来转换类型。但是假如类型不匹配的话,就会抛出ClassCastException。而使用as运算符就会避免这种情况。

    3. Groovy支持操作符重载

      从逻辑上分为三组:比较操作符,算术类操作符,数组类操作符。

      逻辑操作符

      操作符|方法
      ———|————–
      a == b|a.equals(b)
      a <=> b|a.compareTo(b)

      compareTo() 方法返回负整数、0 或正整数,代表对象小于、等于或大于指定对象。因为这个方法能够返回三个值,所以 Groovy 用四个附加值扩充了 <=> 语法,如下所示:

      操作符|含义
      —–|—-
      a > b|如果 a.compareTo(b)返回的值大于 0,那么这个条件为 true
      a >= b|如果compareTo(b)返回的值大于等于 0,那么这个条件为truea < b|如果a.compareTo(b)小于 0,那么这个条件为truea <= b| 如果a.compareTo(b)小于等于 0,那么这个条件为true`

      算术类操作符

      | 操作符 | 方法 |
      | ———- | ————– |
      | a + b | a.plus(b) |
      | a - b | a.minus(b) |
      | a * b | a.multiply(b) |
      | a / b | a.div(b) |
      | a % b | a.mod(b) |
      | a**b | a.power(b) |
      | a++ or ++a | a.next() |
      | a– or –a | a.previous() |
      | a << b | a.leftShift(b) |

      +的常见用法

      用于字符串时,等于调用plus方法,完成两个字符串的拼接,生成新的字符串

      用于列表时,等于调用列表的plus方法,完成两个列表的并集,生成新的列表

      1
      2
      3
      4
      5
      6
      7
      def a = [2, 32, 322]
      //新集合 原集合元素未变 [2, 32, 322, true]
      println(a.plus([true]) )
      def a = -3..4
      def b = 3..5
      //[-3, -2, -1, 0, 1, 2, 3, 4, 3, 4, 5]
      println(a + b)

      -的常见用法

      用于字符串时,A-B就是从A字符串中减去B字符串,生成新字符串,如果A字符串不包含B字符串,生成的字符串还是A

      1
      2
      3
      4
      s1 = 'jack'
      s2 = 'k'
      //jac
      println(s1 - s2)

      用于列表时,A-B,取差集,生成新列表

      1
      2
      3
      4
      def a = -3..4
      def b = 3..5
      //[-3, -2, -1, 0, 1, 2]
      println(a - b)

      *的常见用法

      用于字符串时,字符串A * n(n要>=0)就是生成一个新的字符串,该字符串时重复字符串A n遍。当n=0时,生成空串。

      1
      2
      3
      4
      s1 = 'jack'
      b = s1 * 2
      //jackjack
      println(b)

      用于列表时,列表A * n(n要>=0)就是生成一个新的列表,该列表是A集合的元素重复n遍

      1
      2
      3
      def a = 0..3
      //[0, 1, 2, 3, 0, 1, 2, 3]
      println(a * 2)

      <<的常见用法

      用于StringBuilder、StringBuffer中是添加字符串的意思

      1
      2
      3
      4
      a = new StringBuilder()
      a << 'hello'
      //hello
      println(a.toString())

      用于列表中,向列表中添加新元素

      1
      2
      3
      4
      5
      6
      7
      8
      a = [3, true]
      a << 12
      //[3, true, 12]
      println(a)
      b = [name: 'jack', age: 13]
      b << ['tel': 123123]
      //[name:jack, age:13, tel:123123]
      println(b)

      数组类操作符

      | 操作符 | 方法 |
      | ——– | ————- |
      | a[b] | a.getAt(b) |
      | a[b] = c | a.putAt(b, c) |

      1
      2
      3
      4
      5
      6
      7
      a = [3, true]
      //3 获取第0个元素
      println(a[0])
      //在第2个位置添加元素
      a[2] = 123
      //[3, true, 123]
      println(a)