Groovy基础语法详解

作者:杨龙,叩丁狼教育
本文参考groovy手册,对Groovy的基础语法做一个快速介绍。

注释

单行注释

// a standalone single line comment
println "hello" // a comment till the end of the line

多行注释

/* a standalone multiline comment
   spanning two lines */
println "hello" /* a multiline comment starting
                   at the end of a statement */
println 1 /* one */ + 2 /* two */

Groovy文档注释

GroovyDoc 遵循与 Java 自己的 JavaDoc 相同的约定,可以使用与 JavaDoc 相同的标签。

/**
 * A Class description
 */
class Person {
    /** the name of the person */
    String name

    /**
     * Creates a greeting method for a certain person.
     *
     * @param otherPerson the person to greet
     * @return a greeting message
     */
    String greet(String otherPerson) {
       "Hello ${otherPerson}"
    }
}

Groovy中的关键字

  • 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 name            
def item3
def with_underscore
def $dollarStart

// 错误
def 3tier
def a+b
def a#b

// 正确
foo.as
foo.assert
foo.break
foo.case
foo.catch

引号标识符

引号标识符出现在表达式 . 后面。 例如,person.name 表达式的名称部分可用引号引起。例如 person.“name” 或 person.'name'。
当某些标识符包含被 Java 语言规范禁止的非法字符,但使用引号引起时在 Groovy 中是允许的。 例如,像破折号,空格,感叹号等字符。

def map = [:]

map."an identifier with a space and double quotes" = "ALLOWED"
map.'with-dash-signs-and-single-quotes' = "ALLOWED"

assert map."an identifier with a space and double quotes" == "ALLOWED"
assert map.'with-dash-signs-and-single-quotes' == "ALLOWED"


map.'single quote'
map."double quote"
map.'''triple single quote'''
map."""triple double quote"""
map./slashy string/
map.$/dollar slashy string/$

def firstname = "Homer"
map."Simson-${firstname}" = "Homer Simson" // 支持插值
assert map.'Simson-Homer' == "Homer Simson"

字符串

  • java.lang.String // 不支持插值
  • GStrings (groovy.lang.GString) // 支持插值

单引号字符串

单引号字符串为 java.lang.String,不支持插值。

'ab' == 'a' + 'b' // 字符串拼接

三重单引号字符串

三重单引号字符串为 java.lang.String,不支持插值,三重单引号字符串是多行的,不要用换行符来换行。

def startingAndEndingWithANewline = '''
line one
line two
line three
'''

def strippedFirstNewline = '''\   // 字符串包含换行符作为第一个字符,可以通过用反斜杠转义换行来剥离该字符?
line one
line two
line three
'''
assert !strippedFirstNewline.startsWith('\n')

特殊转移符

Escape sequence Character
'\t' tabulation
'\b' backspace
'\n' newline
'\r' carriage return
'\f' formfeed
'\' backslash
''' single quote (for single quoted and triple single quoted strings)
'"' double quote (for double quoted and triple double quoted strings)

Unicode形式转义符

用于键盘上不存在的字符。

'The Euro currency symbol: \u20AC'

双引号字符串

  • 若没有插值表达式(interpolated expression),则双引号字符串为 java.lang.String,但若存在插值表达式,则为 groovy.lang.GString。
  • 除了单引号和三引号字符串外,任何字符串支持插值。
  • 插值是在对字符串进行评估时用其值替换字符串中的占位符的行为,占位符表达式由 ${}
  • ${} 占位符之间不仅允许表达式,语句也被允许。但语句的值只是 null,因此如果在该占位符中插入了几个语句,最后一个应该以某种方式返回一个有意义的值(不推荐插入语句)。
  • . 表达式使用 $ 占位符,但表达式中若有方法调用,闭包的花括号或算术运算符将无效。
  • 若在 $ 使用转义符号 \,则不有会插值效果。

以下

def name = 'Guillaume' // a plain string
def greeting = "Hello ${name}"
assert greeting.toString() == 'Hello Guillaume'


def sum = "The sum of 2 and 3 equals ${2 + 3}"
assert sum.toString() == 'The sum of 2 and 3 equals 5'

${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'

def number = 3.14
shouldFail(MissingPropertyException) {
    println "$number.toString()" // 语句将抛出一个 groovy.lang.MissingPropertyException,因为 Groovy 认为您正在尝试访问该数字的 toString 属性,该属性不存在,可以理解 Groovy 解析成 "${number.toString}()"。
}

三重双引号字符串

三重双引号字符串表现和双引号字符串一样,另外它是多行的。

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

    You're the winner of the lottery!

    Yours sincerly,

    Dave
"""
assert template.toString().contains('Groovy')

反斜线字符串

  • 使用 / 作为分隔符的斜线字符串。 Slashy 字符串对于定义 正则表达式 特别有用,因为不需要转义反斜杠。
  • 也是多行也能被插值。
  • 一个空的斜线字符串不能用双正斜杠表示,因为 Groovy 解析器被理解为单行注释。
def fooPattern = /.*foo.*/
assert fooPattern == '.*foo.*'

def escapeSlash = /The character \/ is a forward slash/
assert escapeSlash == 'The character / is a forward slash'

def multilineSlashy = /one
    two
    three/
assert multilineSlashy.contains('\n')

def color = 'blue'
def interpolatedSlashy = /a ${color} car/
assert interpolatedSlashy == 'a blue car'

美元转义符

  • Dollar slashy string 是多行 GStrings,分隔以 $/ 开头和以 /$ 结尾。
  • 使用转义符 $ 可以转义 $ 和正斜线 /
def name = "Guillaume"
def date = "April, 1st"

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

    $ dollar sign
    $$ escaped dollar sign
    \ backslash
    / forward slash
    $/ escaped forward slash
    $/$ escaped dollar slashy string delimiter
/$

assert [
    'Guillaume',
    'April, 1st',
    '$ dollar sign',
    '$ escaped dollar sign',
    '\\ backslash',
    '/ forward slash',
        '$/ escaped forward slash',
        '/$ escaped dollar slashy string delimiter'

        ].each { dollarSlashy.contains(it) }

字符串汇总

String name String syntax Interpolated Multiline Escape character
Single quoted '…​' \
Triple single quoted '''…​''' true \
Double quoted "…​" true \
Triple double quoted """…​""" true true \
Slashy /…​/ true true \
Dollar slashy /…​/ true true $

字符

与 Java 不同,Groovy 没有明确的字符字面量。 但可以通过三种不同的方式明确地将 Groovy 字符串设置成字符类型。

char c1 = 'A'
assert c1 instanceof Character

def c2 = 'B' as char
assert c2 instanceof Character

def c3 = (char)'C'
assert c3 instanceof Character

// 当字符被保存在一个变量中时,使用第一种方式好,而当一个 char 值必须作为方法调用的参数传递时使用另外两种方式好。

数字常量

支持的常量类型

  • byte
  • char
  • short
  • int
  • long
  • java.lang.BigInteger
byte  b = 1
char  c = 2
short s = 3
int   i = 4
long  l = 5

// infinite precision
BigInteger bi =  6

若使用 def 关键字声明,则类型取决于该值所兼容的类型。

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

def na = -1
assert na instanceof Integer

// Integer.MIN_VALUE
def nb = -2147483648
assert nb instanceof Integer

// Integer.MIN_VALUE - 1
def nc = -2147483649
assert nc instanceof Long

// Long.MIN_VALUE
def nd = -9223372036854775808
assert nd instanceof Long

// Long.MIN_VALUE - 1
def ne = -9223372036854775809
assert ne instanceof BigInteger

非十进制表示法

二进制表示

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

int xInt = 0b10101111
assert xInt == 175

short xShort = 0b11001001
assert xShort == 201 as short

byte xByte = 0b11
assert xByte == 3 as byte

long xLong = 0b101101101101
assert xLong == 2925l

BigInteger xBigInteger = 0b111100100001
assert xBigInteger == 3873g

int xNegativeInt = -0b10101111
assert xNegativeInt == -175

八进制表示

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

int xInt = 077
assert xInt == 63

short xShort = 011
assert xShort == 9 as short

byte xByte = 032
assert xByte == 26 as byte

long xLong = 0246
assert xLong == 166l

BigInteger xBigInteger = 01111
assert xBigInteger == 585g

int xNegativeInt = -077
assert xNegativeInt == -63

十六进制表示

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

int xInt = 0x77
assert xInt == 119

short xShort = 0xaa
assert xShort == 170 as short

byte xByte = 0x3a
assert xByte == 58 as byte

long xLong = 0xffff
assert xLong == 65535l

BigInteger xBigInteger = 0xaaaa
assert xBigInteger == 43690g

Double xDouble = new Double('0x1.0p0')
assert xDouble == 1.0d

int xNegativeInt = -0x77
assert xNegativeInt == -119

小数

  • float

  • double

  • java.lang.BigDecimal

  • Groovy 使用 BigDecimal 是小数的默认值。 另外,floatdouble 都支持,但需要一个明确的类型声明、类型强制或后缀。

  • 在方法或闭包中接受一个类型 BigDecimal 类型的值,即使方法参数类型是 floatdouble

  • 不能使用二进制,八进制或十六进制表示来表示小数。

// primitive types
float  f = 1.234
double d = 2.345

// infinite precision
BigDecimal bd =  3.456

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

下划线在数字常量中的使用

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

数值类型强制表示

使用后缀强制一个数值的类型:

Type Suffix
BigInteger G or g
Long L or l
Integer I or i
BigDecimal G or g
Double D or d
Float F or f
assert 42I == new Integer('42')
assert 42i == new Integer('42') // lowercase i more readable
assert 123L == new Long("123") // uppercase L more readable
assert 2147483648 == new Long('2147483648') // Long type used, value too large for an Integer
assert 456G == new BigInteger('456')
assert 456g == new BigInteger('456')
assert 123.45 == new BigDecimal('123.45') // default BigDecimal type used
assert 1.200065D == new Double('1.200065')
assert 1.234F == new Float('1.234')
assert 1.23E23D == new Double('1.23E23')
assert 0b1111L.class == Long // binary
assert 0xFFi.class == Integer // hexadecimal
assert 034G.class == BigInteger // octal

计算结果类型判定

byte char short int long BigInteger float double BigDecimal
byte int int int int long BigInteger double double BigDecimal
char int int int long BigInteger double double BigDecimal
short int int long BigInteger double double BigDecimal
int int long BigInteger double double BigDecimal
long long BigInteger double double BigDecimal
BigInteger BigInteger double double BigDecimal
float double double double
double double double
BigDecimal BigDecimal
  • 运算结果类型取决于表达中类型数值范围最大的,但任何与 floatdouble 运算结果都是 double

除法操作

  • 正数相除可以使用 / 或方法 intdiv()。
  • BigDecimal 类型相除使用方法 divide。
def a = 2.intdiv(2)     // 1
def b = 2.0.divide(1.0) // 1
def c = 2.000000.divide(1.00, 1, RoundingMode.HALF_UP) // 2.0

幂运算

幂运算符由 ** 运算符表示,其中有两个参数:基数和指数。 幂运算符的结果取决于其操作数和操作结果。

  • 指数是小数

    • 若结果可以为 Integer 类型,则返回是 Integer 类型值。
    • 若结果可以为 Long 类型,则返回 Long 类型值。
    • 其它情况返回 Double 类型值。
  • 指数是正数

    • 负数
      • 若结果值适合该类型,则返回 IntegerLongDouble 类型值。
    • 正数或 0
      • 若基数是 BigDecimal 类型,则返回是 BigDecimal 类型值。
      • 若基数是 BigInteger 类型,则返回是 BigInteger 类型值。
      • 若基数是 Integer 类型,且结果值适合该类型,则返回 IntegerBigInteger 类型值。
      • 若基数是 Long 类型,且结果值适合该类型,则返回 LongBigInteger 类型值。
// base and exponent are ints and the result can be represented by an Integer
assert    2    **   3    instanceof Integer    //  8
assert   10    **   9    instanceof Integer    //  1_000_000_000

// the base is a long, so fit the result in a Long
// (although it could have fit in an Integer)
assert    5L   **   2    instanceof Long       //  25

// the result can't be represented as an Integer or Long, so return a BigInteger
assert  100    **  10    instanceof BigInteger //  10e20
assert 1234    ** 123    instanceof BigInteger //  170515806212727042875...

// the base is a BigDecimal and the exponent a negative int
// but the result can be represented as an Integer
assert    0.5  **  -2    instanceof Integer    //  4

// the base is an int, and the exponent a negative float
// but again, the result can be represented as an Integer
assert    1    **  -0.3f instanceof Integer    //  1

// the base is an int, and the exponent a negative int
// but the result will be calculated as a Double
// (both base and exponent are actually converted to doubles)
assert   10    **  -1    instanceof Double     //  0.1

// the base is a BigDecimal, and the exponent is an int, so return a BigDecimal
assert    1.2  **  10    instanceof BigDecimal //  6.1917364224

// the base is a float or double, and the exponent is an int
// but the result can only be represented as a Double value
assert    3.4f **   5    instanceof Double     //  454.35430372146965
assert    5.6d **   2    instanceof Double     //  31.359999999999996

// the exponent is a decimal value
// and the result can only be represented as a Double value
assert    7.8  **   1.9  instanceof Double     //  49.542708423868476
assert    2    **   0.1f instanceof Double     //  1.0717734636432956

布尔

  • truefalse 是唯一的两个原始布尔值。
  • Groovy 有一个特殊的规则(通常称为Groovy Truth),用于将非布尔对象强制为一个布尔值。
def myBooleanVariable = true
boolean untypedBooleanVar = false
booleanField = true

列表

  • Groovy 使用 , 分隔的值,用 [] 括起来表示 lists。
  • 因为 Groovy 没有定义自己的集合类,Groovy lists 是 JDK java.util.List,默认使用的实现类是 java.util.ArrayList,除非手动通过操作符 as 和声明类型指定。
  • lists 可以存放多种类型元素。
def numbers = [1, 2, 3]
assert numbers instanceof List  
assert numbers.size() == 3  

def heterogeneous = [1, "a", true]

def arrayList = [1, 2, 3]
assert arrayList instanceof java.util.ArrayList

def linkedList = [2, 3, 4] as LinkedList    
assert linkedList instanceof java.util.LinkedList

LinkedList otherLinked = [3, 4, 5]          
assert otherLinked instanceof java.util.LinkedList
  • 可通过索引读取和设置值访问 list 中的元素,可用 << leftShift 运算符将元素附加到 list 中。
  • 索引范围的值:[-list.size(), list.size() - 1]小于最小索引获取元素报索引越界,大于最大索引获取元素是 null(这点不同 Java)
  • list 元素可以是另一个 list。
def letters = ['a', 'b', 'c', 'd']

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

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

letters[2] = 'C'             
assert letters[2] == 'C'

letters << 'e'                          // 添加元素到 list 尾部。              
assert letters[4] == 'e'
assert letters[-1] == 'e'               // 注意这个 索引 -1 上的值是最后一个元素

assert letters[1, 3] == ['b', 'd']      // 注意这个返回一个新的 list   
assert letters[2..4] == ['C', 'd', 'e'] // 注意这个返回一个新的 list

def multi = [[0, 1], [2, 3]]     
assert multi[1][0] == 2  

数组

  • 定义数组 Groovy 重用 list 定义方式,若要明确数组类型可通过 as 强制或类型声明。
String[] arrStr = ['Ananas', 'Banana', 'Kiwi']  // 类型声明

assert arrStr instanceof String[]    
assert !(arrStr instanceof List)

def numArr = [1, 2, 3] as int[]                 // `as` 强制   

assert numArr instanceof int[]       
assert numArr.size() == 3
  • 支持定义多维数组。
  • Groovy 不支持 Java 数组初始化符号 {},因为 {} 可能会被曲解成 Groovy 闭包的符号。
def matrix3 = new Integer[3][3]         
assert matrix3.size() == 3

Integer[][] matrix2                     
matrix2 = [[1, 2], [3, 4]]
assert matrix2 instanceof Integer[][]

String[] names = ['Cédric', 'Guillaume', 'Jochen', 'Paul']
assert names[0] == 'Cédric'     

names[2] = 'Blackdrag'          
assert names[2] == 'Blackdrag'

Maps

  • Groovy 具有 maps 关联键映射到值,使用冒号分隔键和值,并使用逗号分隔每个键/值对,以及由方括号包围的整个键和值。
  • Groovy 创建的 map 使用的是 java.util.LinkedHashMap 的实例。
def colors = [red: '#FF0000', green: '#00FF00', blue: '#0000FF']   

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

colors['pink'] = '#FF00FF'          // 设值           
colors.yellow  = '#FFFF00'          // 设值          

assert colors.pink == '#FF00FF'
assert colors['yellow'] == '#FFFF00'

assert colors instanceof java.util.LinkedHashMap

assert colors.unknown == null       // 不存在 key 返回 null
  • 可以使用其他类型值作为键。
def numbers = [1: 'one', 2: 'two']
assert numbers[1] == 'one'

def key = 'name'
def persons = [key: 'Guillaume']      

assert !persons.containsKey('name')
assert persons.containsKey('key')

person = [(key): 'Guillaume']        

assert person.containsKey('name')    
assert !person.containsKey('key')
  • 若变量或表达式作为 map 中的键使用,必须用括号括住变量或表达式
def key = 'name'
def persons = [(key): 'Guillaume']        

assert persons.containsKey('name')    
assert !persons.containsKey('key')
WechatIMG9.jpeg
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,132评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,802评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,566评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,858评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,867评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,695评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,064评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,705评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,915评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,677评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,796评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,432评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,041评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,992评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,223评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,185评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,535评论 2 343

推荐阅读更多精彩内容