Groovy语言基础语法

简介

Groovy是一种基于JVM的敏捷开发语言,结合了Python、Ruby和Smalltalk的许多强大的特性。Groovy在语法上支持动态类型、闭包等新一代语言特性,它能够无缝集成所有已经存在的Java类库,即支持面向对象也支持面向过程编程。

变量

变量类型分为基本类型和对象类型,但是groovy中所有的类型都是对象类型,基本类型其实是包装类。变量可以使用强类型定义,也可以使用def方式的弱类型定义;强类型就是指定的类型,def定义的类型其实是声明成了Object类型。

double num = 10.0
// java.lang.Double
println num.class

def x = 10
// java.lang.Integer
println x.class

x = "Hello groovy"
// java.lang.String
println x.class

字符串

String类和Java中的String类基本上相同,不在详细介绍。GString是Groovy里添加的新字符串类型,功能很强大。字符串定义使用单引号形式的字符串,还有一种三引号格式的字符串可以保留字符串格式,这两种字符串都不能被修改。

def name = 'str'
// java.lang.String
println name.class

def tripleName = '''three 
single 
string'''
// 打印字符串保留了换行
println tripleName
// java.lang.String
println tripleName.class

还有一种双引号的字符串,默认情况是String类型,如果包含参数模板那么就会变成GString类型。在开发过程中不必特意转换String和GString,编译器会帮助开发者做转换操作。

def text = "Android"
// java.lang.String
println text.class
def sayHello = "Hello: $text"
println sayHello
// GStringImpl
println sayHello.class

def product = "The product of 3 * 4 = ${3 * 4}"
println product
// GStringImpl
println product.class

// GStringImpl自动转换成String,不会报错,能成功运行
def result = echo(product)
println result

// 可以接受String类型,也可以接受GStringImpl类型
String echo(String message) {
    return message
}

GString包含了String中所有的方法,还有DefaultGroovyMethods是Groovy针对Java中所有类型扩展的新方法,最后还包含了StringGroovyMethods继承了DefaultGroovyMethods并重写了一些方法使之更加适合字符串操作。

def name = 'string'
println name.center(10, '$') 
println name.padLeft(10, '$')
println name.padRight(10, '$')
println name[2]
println name[2..5]

输出:
$$string$$
$$$$string
string$$$$
r
ring

逻辑控制

Groovy中的判定结构if和Java中的基本一致,switch语句则比Java中的要强大很多,switch支持各种类型的判断,不再局限于整数、字符串和enum类型了。循环for和while语句和Java中使用方式基本上一致,不过针对List和Map,Groovy的for循环采用了in操作符来做遍历。

def x = 1
switch (x) {
    case 2:
        break
    case 'Hello':
        break
    case Integer.class:
        println x
        break
    case [1, 3, 5]:
        break
    case ['k1': 'v1', 'k2' : 'v2' ]:
        break
    default:
        break
}
// 1

for (it in [1, 3, 5]) {
    println it
}
// 1
// 3
// 5

for (it in ['k1': 'v1', 'k2' : 'v2' ]) {
    println it.key + it.value
}
// k1v1
// k2v2

闭包

闭包就是一个代码块,使用{}包裹,简单的闭包定义就是大括号加里面的代码块,可以通过闭包的call方法或者加()直接调用,类似于函数调用。闭包如果只有一个变量,可以在声明中忽略这个参数,直接在使用时用it关键字。

def call = { println 'Hello World' }
call.call()
call()

def closure = { String name -> println "Hello $name" }
// def closure = { println "Hello $it" }
closure.call("World")
closure("World")

// Hello World

闭包一定会有返回值,如果闭包最后没有return语句,那么返回值就是最后的表达式返回值,如果表达式返回void,那么闭包返回的就是null。

def closure = { println "Hello $it" }
def result = closure("World")
// null
println result

闭包的三个重要变量this,owner,delegate,其中this代表定义闭包的这个类;owner代表闭包定义处的类,也可以代表定义处的对象,因为闭包可以定义在闭包里,这是嵌套闭包的owner就是外部的闭包对象;delegate可以是任意的值,默认情况下就是owner的值。

def closure = {
    println "Closure this: " + this
    println "Closure owner: " + owner
    println "Closure delegate: " + delegate
}
closure()

def call = {
    def myclosure = {
        println "MyClosure this: " + this
        println "MyClosure owner: " + owner
        println "MyClosure delegate: " + delegate
    }

    myclosure()
}

call()

总之,this、owner、delegate在类或者方法中定义的时候它们默认都是一样的,都是定义它们所在的类或类对象,如果在闭包中定义闭包那么this还是当前定义的类或类队形,而owner和delegate会指向外部闭包对象;owner和delegate默认情况下都是一样的,但是用户手动修改delegate之后,owner和delegate就会不一致的,还有this和owner是无法修改的。

class Person {
    String name
    def print = {
        println "Hello $name"
    }

    String toString() {
        print()
    }
}

class User {
    String name
}

def person = new Person(name: 'Person')
def user = new User(name: 'User')

// Hello Person
person.toString()
person.print.delegate = user
person.print.resolveStrategy = Closure.DELEGATE_FIRST
// Hello User
person.toString()

闭包委托策略:

策略值 意义
OWNER_FIRST 首先从owner对象上查找
DELEGATE_FIRST 首先从delegate对象上查找
OWNER_ONLY 只从owner上查找
DELEGATE_ONLY 只从delegate对象上查找
TO_SELF 只从当前闭包对象上查找

List使用

List的定义很简单只需要使用中括号并且用逗号分隔每个元素就可以了。不过这种定义方式同样适用于数组类型,可以通过as关键字或者直接在定义变量的时候制定数组类型来定义数组。

def list = [1, 2, 3, 4, 5]
println list.toListString()
println list.class

int[] intArr = [1, 2, 3, 4, 5]
println intArr.toString()
println intArr.class
// [1, 2, 3, 4, 5]
// class java.util.ArrayList

def arr = [1, 2, 3, 4, 5 ] as int[]
println arr.class

// [1, 2, 3, 4, 5]
// class [I
// class [I

通过上面的示例代码可以看出List默认情况下是ArrayList类型,Groovy里的列表对象底层实现其实是数组实现。和Java8类似Groovy的List对象也包含了各种函数式编程扩展方法。

def list = [1, 2, 3, 4, 5]

// 遍历每个元素
list.each {
    println it
}
// 12345

// 找到第一个符合条件的元素
println list.find { it % 2 == 0 }
// 2

// 找到所有符合条件的元素
println list.findAll {
    it % 2 == 0
}
// [2, 4] 

// 是否存在某个元素符合该条件
println list.any { it % 2 == 0 }
// true

// 列表中每个元素都符合该条件
println list.every { it % 2 == 0 }
// false

// 根据条件将列表分组
println list.groupBy {
    it % 2 == 0
}
// [false:[1, 3, 5], true:[2, 4]]

Map

Map的定义也是使用中括号加逗号分隔元素,不过Map的元素是key:value这种类型的,如果key是不可变的String类型,那么可以直接省略两边的引号。

def map = [key1 : 'user1', key2 : 'user2', key3: 'user3']
println map['key1']
println map.key1
// user1

上面的定义使用起来非常简单,和List类似,Map也扩展了很多有用的函数式变成接口。

def map = [key1 : 'user1', key2 : 'user2', key3: 'user3']
map.each {
    println "${it.key} = ${it.value}"
}
// key1 = user1
// key2 = user2
// key3 = user3

println map.find {
    it.key == 'key2'
}
// key2=user2

println map.findAll {
    it.key.startsWith('key')
}
// [key1:user1, key2:user2, key3:user3]

map.eachWithIndex { Map.Entry<String, String> entry, int i ->
    println "$i ${entry.key} = ${entry.value}"
}
// 0 key1 = user1
// 1 key2 = user2
// 2 key3 = user3

println map.any {
    it.value == 'user2'
}
// true

println map.every {
    it.value == 'user2'
}
// false

println map.groupBy {
    it.value
}
// [user1:[key1:user1], user2:[key2:user2], user3:[key3:user3]]

Range

Range范围是List的一种实现,不过它主要是针对int类型的数据,可以使用..简单地定义范围对象,还可以在..前后添加符号声明是否包含边界值。

def range = 1..10
def range2 = 1..<10

range.each {
    println it
}
// 1 2 3 4 .. 8 9 10
range2.each {
    println it
}
// 1 2 3 .. 8 9

由于Range继承自List,对于List相关的扩展方法同样适用于Range,这里不再赘述。

面向对象

Groovy的类接口定义和Java基本一致,不过Groovy的类可以不加访问控制权限,默认情况下都是public访问权限。

class Person {
    String name
    int age
    String toString() {
        return "name = $name, age = $age"
    }
}

def person = new Person(name: 'zhangsan', age: 30)
println person.toString()

Java8之前的interface都只能包含抽象方法,不能有方法实现,不过Java8为了lambda表达式增加了默认方法。Groovy的interface类似于Java8之前的接口,只有抽象接口方法;trait类似于Java8中的接口,既可以包含抽象接口也可以包含默认实现方法,它也可以用来实现。

trait Movable {
    abstract void move()

    String description() {
        return "I can move"
    }
}

class Car implements Movable {

    @Override
    void move() {
        println "Moving ..."
    }
}

def car = new Car()
car.move()
println car.description()

Groovy面向对象有一个强大的特性,也就是元编程,它能支持运行时动态添加类的属性和方法。默认情况下只在添加属性和方法的类中有效,可以通过调用ExpandoMetaClass.enableGlobally()使添加属性和方法全局有效。

class Car {

    void move() {
        println "Moving ..."
    }
}

def car = new Car()
car.metaClass.start = {
    -> println "start the engine"
}
car.metaClass.wheelCount = 4

car.move()
car.start()
println car.wheelCount

上面的MetaClass元编程能够在运行时动态添加属性和方法,这也就是为什么编译时调用对象不存在的方法也不报错的原因。如果用户没有为类添加需要的方法,如果类覆盖了methodMissing方法,那么这个方法就会被调用,如果类没有覆盖这个方法但是覆盖了invokeMethod方法,那么invokeMethod方法会被调用。如果以上两个方法都没有被覆盖,最后会在运行时跑出MethodMissingException异常并终止应用。

猜你喜欢

转载自blog.csdn.net/xingzhong128/article/details/80043190
今日推荐