Groovy 基础 - 语法

2018-04-12 笔记 #Groovy

介绍

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系统用的,用于声明脚本运行的环境。

关键字

as assert break case
catch class const continue
def default do else
enum extends false finally
for goto if implements
import in instanceof interface
new null package return
super switch this throw
throws trait true try
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

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

类型 后缀
BigInteger G 或 g
Long L 或 l
Integer I 或 i
BigDecimal G 或 g
Double D 或 d
Float F 或 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
更新时间: 2019-03-04
许可协议:CC by-nc-nd 4.0