Groovy 基础 - 语法

2018-04-12 [编程语言] #Groovy #Notes
更新日志
2021-06-11 更新分类、标签

介绍

Groovy…

以下内容大部分来自官方文档(version: 2.4.15),可以理解为删减后的翻译版…

语法

Groovy是一个基于Java 虚拟机的语言,Groovy语法特性源自于Java并参考了Ruby、Python、Smalltalk等语言的特性

注释

因为在Groovy中可以完全使用Java语法,所以在Groovy中,注释与Java一致,其中唯一不同的是Groovy增加了一个特殊的注释:

#!/usr/bin/env groovy
println "Hello from the shebang line"

这个注释主要给unix系统用的,用于声明脚本运行的环境。

关键字

asassertbreakcase
catchclassconstcontinue
defdefaultdoelse
enumextendsfalsefinally
forgotoifimplements
importininstanceofinterface
newnullpackagereturn
superswitchthisthrow
throwstraittruetry
while

命名规则

标识符以字母,$或下划线开头。不能以数字开头。

在定义变量时,可以不必指明变量类型,直接使用 def关键字或者不写来声明


// 使用def
def name = "张三"

// 直接定义
age = 20

// 使用类型
boolean male = true

在Groovy中,存在一种比较特殊的标识符:引号标识符。引号标识符用于解决在Java语法规范中禁止但Grovvy却支持的操作

// 定义一个Map
def map = [:]

// Map允许以.的方式调用,但由于Map的Key存在空格, Java语法将直接报错,所以groovy使用引号来处理
map."an identifier with a space and double quotes" = "ALLOWED"
map.'with-dash-signs-and-single-quotes' = "ALLOWED"

与Java 的不同

默认引入的包

在Groovy中,默认引入了以下包:

所以在代码中时,可以直接使用对象而不用引包

其余不同可以看官方文档

字符串

Groovy的字符串特别强大,提供了各种有意思的特性。Groovy的字符串有两个对象:java.lang.Stringgroovy.lang.GString,其中 GString 是一种升级的String类型,支持类似与模板变量的定义。

单引号

在Java中,单引号用于标识char类型,而在Groovy中,则可以直接与这样社用。单引号是不支持 插值(下面会讲到),仅仅是一个普通的String 类型

'这是单引号符串'

三引号

三引号字符串在Python中是很常见的,有时候写Java代码需要支持换行时,就非常苦恼,基本都采用+号连接。而在Groovy中,则可以直接与Python一样,使用三引号处理对行内容。

三引号字符串有点类似于HTML中的pre标签,如果三引号中存在缩进,使用println时,将会原封不动输出,也就是说,三引号会保留缩进内容。

'''
    行1
    行2
    这是三引号
'''

双引号

双引号字符串中,如果双引号中没有包含插值,则双引号字符串类型是 java.lang.String 类型,如果包含插值,则双引号字符串类型是 groovy.lang.GString

插值

插值实际是一种字符串的模板变量,也可以理解为占位符,插值使用${}包含变量名。插值是不可以在单引号和三引号中使用的,如果其中有插值,将不会被编译,而直接字面输出。

当字符串包含插值时,类型自动转换为 groovy.lang.GString 可以使用 toString() 方法转换为String类型(相当于执行完模板变量并返回最终的值)

def name = '张三'
def helloStr = "Hello , ${name} ."

// 插值中可以直接使用表达式
def str2 = helloStr + "I am ${1 + 2 + 3 + 4} years old"

// 插值也可以支持语句
println "Hello, ${def a = 1; def b = 2; a + b}"

除了使用${}来定义插值,还可以直接使用$符号来使用变量,但不可以用$来使用方法和算术运算符

def person = [name: 'Guillaume', age: 36]
assert "$person.name is $person.age years old" == 'Guillaume is 36 years old'
插值使用闭包

闭包的概念可以先简单理解为一个没有方法声明的方法体内容(并不一定正确,但大致可以这样理解来看)

在Groovy中使用闭包作为插值,那么插值的最终结果是在每次调用该字符串时产生,而不是声明时产生。可以看一段官方实例代码理解

def number = 1 

// 普通插值
def eagerGString = "value == ${number}"
// 使用闭包
def lazyGString = "value == ${ -> number }"

assert eagerGString == "value == 1" 
assert lazyGString ==  "value == 1" 

number = 2 
assert eagerGString == "value == 1" 
assert lazyGString ==  "value == 2" 

上面实例代码中,lazyGString 变量会随着闭包引用变量的变化而变化,在官方的实例中,很形象得使用了:lazy来定义变量名,该插值会每次在调用时得到最终的字符串结果。

与Java代码糅合

因为当字符串使用闭包时,字符串类型将从 java.lang.String 转换为 groovy.lang.GString ,当一个Java或者Groovy方法需要一个 java.lang.String 类型的参数时,传入 groovy.lang.GString 将会自动转换为 java.lang.String

hashCode

因为 groovy.lang.GString 的字符串内容是动态生成的,即便最终生成的值与 String 类型的值一致,它们的hashCode也不同。

assert "one: ${1}".hashCode() != "one: 1".hashCode()

所以,因为上述结论,在Map中不应使用 String 来作为Key,如果使用 GString 来作为Key,使用字符串取值的时候,会找不到该Key。

三重双引号

三重双引号与双引号的作用类似,可以说是双引号和三引号的结合版,支持双引号和三引号的特性。

def name = 'Groovy'
def template = """
    Dear Mr ${name},

    You're the winner of the lottery!

    Yours sincerly,

    Dave
"""

斜杠字符串

在groovy中可以使用斜杠来定义字符串,使用斜杠字符串可以当双引号、三引号使用,也支持插值。斜杠字符串不需要转移反斜杠,可以更加方便编写正则表达式。

def hello = /Hello/

美元斜杠字符串(Dollar slashy string)

美元斜杠通过 $/ 和 /$ 定义字符串,主要用于字符串中存在多种需要转义符号的情况下,支持多行、可以使用插值。

def name = "Groovy"
def date = "April, 1st"

def dollarSlashy = $/
    Hello $name,
    today we're ${date}.

    $ dollar sign
    $$ escaped dollar sign
    \ backslash
    / forward slash
    $/ escaped forward slash
    $$$/ escaped opening dollar slashy
    $/$$ escaped closing dollar slashy
/$

Characters

很有意思的是,在Groovy中没有明确的字符串定义,因为单引号可以用于定义字符串。但是可以通过以下三种方式来声明一个char类型

// 使用char类型来声明
char c1 = 'A'

// 使用as转换为char类型
def c2 = 'B' as char

// 强制转换为char类型
def c3 = (char)'C'

数字

Groovy中可以使用Java的数字类型,在声明数字类型时,Groovy会根据数字的大小自动转换成相应的类型。

def a = 1
assert a instanceof Integer

// Integer.MAX_VALUE
def b = 2147483647
assert b instanceof Integer

// Integer.MAX_VALUE + 1
def c = 2147483648
assert c instanceof Long

// Long.MAX_VALUE
def d = 9223372036854775807
assert d instanceof Long

// Long.MAX_VALUE + 1
def e = 9223372036854775808
assert e instanceof BigInteger

可以使用 ** 来做乘方运算。

2    **   3     // 8
10   **  -1     // 0.1

其他进制的数字

二进制

二进制数字以前缀 : 0b 开头

int xInt = 0b10101111
assert xInt == 175
八进制

八进制数字以前缀:0 开头

int xInt = 077
assert xInt == 63
16进制

16进制数字以前缀:0x 开头

int xInt = 0x77
assert xInt == 119

浮点数

在Groovy中,浮点数的默认类型为BigDecimal,不过在方法参数是double或者float时,依旧可以传入BigDecimal

浮点数支持使用指数

assert 1e3 == 1_000.0
assert 2E4 == 20_000.0
assert 3e+1 == 30.0
assert 4E-2 == 0.04
assert 5e-1 == 0.5

在编写较长的数字时,Groovy允许使用下划线来分组数字,有点类似于我们数字里面常用的逗号

long creditCardNumber = 1234_5678_9012_3456L
long socialSecurityNumbers = 999_99_9999L
double monetaryAmount = 12_345_132.12
long hexBytes = 0xFF_EC_DE_5E
long hexWords = 0xFFEC_DE5E
long maxLong = 0x7fff_ffff_ffff_ffffL
long alsoMaxLong = 9_223_372_036_854_775_807L
long bytes = 0b11010010_01101001_10010100_10010010

在定义数字时,可以通过给值定一个后缀强制定义为指定类型

类型后缀
BigIntegerG 或 g
LongL 或 l
IntegerI 或 i
BigDecimalG 或 g
DoubleD 或 d
FloatF 或 f

Lists

在Groovy中,集合默认使用的类型是 java.util.List ,因为Groovy中没有自己实现集合类型。

使用Java中数组的形式定义集合

def numbers = [1, 2, 3]
assert numbers instanceof List
assert numbers.size() == 3

// 类似于Java中二位数组的集合
def multi = [[0, 1], [2, 3]]
assert multi[1][0] == 2

通常,我们定义集合时会使用同一种类型,Groovy也提供了不同类型的集合定义

def values = [22, "张三", true]

在集合时,可以使用Groovy中的关键字as指定集合的类型

def linkedList = [2, 3, 4] as LinkedList

取值

可以直接使用下标来取值,其中下标分为两种, 正向取值和逆向取值。正向取值意思是使用正数从前往后取,逆向取值意思是使用复数从后往前取

def letters = ['a', 'b', 'c', 'd']

assert letters[0] == 'a'     
assert letters[1] == 'b'

assert letters[-1] == 'd'    
assert letters[-2] == 'c'

// 同时访问2个元素
assert letters[1, 3] == ['b', 'd']

// 访问指定范围的元素
assert letters[2..4] == ['C', 'd', 'e']

赋值

给List某个元素赋值直接使用=号即可

letters[2] = 'C'

添加

使用 **«**给集合添加元素

letters << 'e'

数组

数组与List的定义方式一样,只不过在定义时需要指定类型

String[] arrStr = ['Ananas', 'Banana', 'Kiwi']
def numArr = [1, 2, 3] as int[]

Map

Map的定义也是使用中括号,这与Groovy中大括号的使用有关:大括号用于闭包。所以可以使用如下方式创建一个Map

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

Map支持使用[key] 和 以对象打点的方式进行调用。

assert colors['red'] == '#FF0000'
assert colors.green  == '#00FF00'

支持使用变量作为Key,但需要使用()将变量包起来


def key = 'name'
// 定义一个包含key为name的Map
person = [(key): 'Guillaume']
文章作者:eightpigs
创作时间:2018-04-12
更新时间:2021-06-11
许可协议:CC by-nc-nd 4.0