groovy语法
1.注释
1.1. 单行注释
1.2. 多行注释
1.3. GroovyDoc注释
1.4. Shebang线
2.关键词
3.标识符
3.1. 普通标识符
3.2. 带引号的标识符
4.字符串
4.1. 单引号字符串
4.2. 字符串连接
4.3. 三重单引号字符串
4.3.1. 逃避特殊字符
4.3.2. Unicode转义序列
4.4. 双引号字符串
4.4.1. 字符串插值
4.4.2. 插值闭包表达式的特例
4.4.3. 与Java的互操作性
4.4.4. GString和String hashCodes
4.5. 三重双引号字符串
4.6. Slashy string
4.7. 美元邋string的字符串
4.8. 字符串摘要表
4.9. 人物
5.数字
5.1. 积分文字
5.1.1. 替代的非基础10表示
二进制文字
八进制文字
十六进制文字
5.2. 十进制文字
5.3. 文字中的下划线
5.4. 数字类型后缀
5.5. 数学运算
5.5.1. 除法运算符的情况
5.5.2. 电力运营商的情况
6.布尔
7.列表
8.数组
9.map
句法
本章介绍Groovy编程语言的语法。语言的语法源自Java语法,但使用Groovy的特定构造对其进行了增强,并允许某些简化。
1.注释
1.1。单行注释
单行注释以行开头,//
可以在行中的任何位置找到。//
直到行尾的字符被视为注释的一部分。
// a standalone single line comment
println "hello" // a comment till the end of the line
1.2。多行注释
多行注释以行开头,/*
可以在行中的任何位置找到。以下字符/*
将被视为注释的一部分,包括换行符,直到第一个*/
结束注释。因此,多行注释可以放在语句的末尾,甚至可以放在语句中。
/* a standalone multiline comment
spanning two lines */
println "hello" /* a multiline comment starting
at the end of a statement */
println 1 /* one */ + 2 /* two */
1.3。GroovyDoc注释
与多行注释类似,GroovyDoc注释是多行的,但从一开始就/**
结束*/
。第一个GroovyDoc注释行后面的行可以选择以星号开头*
。这些注释与:
-
类型定义(类,接口,枚举,注释),
-
字段和属性定义
-
方法定义
虽然编译器不会抱怨GroovyDoc注释与上述语言元素没有关联,但是你应该在它之前添加带有注释的那些结构。
/**
* 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}"
}
}
GroovyDoc遵循与Java自己的JavaDoc相同的约定。因此,您将能够使用与JavaDoc相同的标记。
1.4。Shebang线
除了单行注释之外,还有一个特殊的行注释,通常称为UNIX系统可以理解的shebang行,它允许脚本直接从命令行运行,只要您已经安装了Groovy发行版并且该groovy
命令可用于PATH
。
#!/usr/bin/env groovy
println "Hello from the shebang line"
该# 字符必须是文件的第一个字符。任何缩进都会产生编译错误。 |
2.关键词
以下列表表示Groovy语言的所有关键字:
Table 1. Keywords
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
|
3.标识符
3.1。普通标识符
标识符以字母,美元或下划线开头。他们不能以数字开头。
一个字母可以是以下范围内:
-
'a'到'z'(小写ascii字母)
-
'A'到'Z'(大写ascii字母)
-
'\ u00C0'到'\ u00D6'
-
'\ u00D8'到'\ u00F6'
-
'\ u00F8'到'\ u00FF'
-
'\ u0100'到'\ uFFFE'
然后跟随字符可以包含字母和数字。
以下是有效标识符的一些示例(此处为变量名称):
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
3.2。带引号的标识符
带引号的标识符出现在虚线表达式的点后面。例如,表达式的name
一部分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"
正如我们将在下面的字符串部分中看到的,Groovy提供了不同的字符串文字。点后实际允许所有类型的字符串:
map.'single quote'
map."double quote"
map.'''triple single quote'''
map."""triple double quote"""
map./slashy string/
map.$/dollar slashy string/$
普通字符串和Groovy的GStrings(插值字符串)之间存在差异,因为在后一种情况下,插值将插入到最终字符串中以评估整个标识符:
def firstname = "Homer"
map."Simpson-${firstname}" = "Homer Simpson"
assert map.'Simpson-Homer' == "Homer Simpson"
4.字符串
文本文字以称为字符串的字符串的形式表示。Groovy允许您实例化java.lang.String
对象,以及GStrings(groovy.lang.GString
),它们在其他编程语言中也称为插值字符串。
4.1。单引号字符串
单引号字符串是由单引号括起来的一系列字符:
'a single quoted string'
单引号字符串是普通的java.lang.String ,不支持插值。 |
4.2。字符串连接
所有Groovy字符串都可以与+
运算符连接:
assert 'ab' == 'a' + 'b'
4.3。三重单引号字符串
三重单引号字符串是由单引号三元组包围的一系列字符:
'''a triple single quoted string'''
三重单引号字符串是普通的java.lang.String ,不支持插值。 |
三重单引号字符串是多行的。您可以跨越行边界跨越字符串的内容,而无需将字符串拆分为多个部分,而不使用连接或换行转义字符:
def aMultilineString = '''line one
line two
line three'''
如果您的代码是缩进的,例如在类方法的主体中,则字符串将包含缩进的空格。Groovy开发工具包包含使用该String#stripIndent()
方法去除缩进的方法,以及使用String#stripMargin()
分隔符来识别要从字符串开头删除的文本的方法。
创建字符串时如下:
def startingAndEndingWithANewline = '''
line one
line two
line three
'''
您会注意到结果字符串包含换行符作为第一个字符。可以通过使用反斜杠转义换行来删除该字符:
def strippedFirstNewline = '''\
line one
line two
line three
'''
assert !strippedFirstNewline.startsWith('\n')
4.3.1。逃避特殊字符
您可以使用反斜杠字符转义单引号,以避免终止字符串文字:
'an escaped single quote: \' needs a backslash'
你可以使用双反斜杠来逃避转义字符本身:
'an escaped escape character: \\ needs a double backslash'
一些特殊字符也使用反斜杠作为转义字符:
逃脱序列 | 字符 |
---|---|
'\ t' |
制表 |
'\ B' |
退格 |
'\ n' |
新队 |
'\ r' |
回车 |
'\F' |
换页符 |
'\\' |
反斜线 |
'\'” |
单引号(单引号和三引号字符串) |
'\“' |
双引号(双引号和三引号双引号) |
4.3.2。Unicode转义序列
对于键盘上不存在的字符,可以使用unicode转义序列:反斜杠,后跟“u”,然后是4个十六进制数字。
例如,欧元货币符号可以表示为:
'The Euro currency symbol: \u20AC'
4.4。双引号字符串
双引号字符串是由双引号括起来的一系列字符:
"a double quoted string"
java.lang.String 如果没有插值表达式, 双引号字符串是普通的,但如果存在插值则是groovy.lang.GString 实例。 |
要转义双引号,可以使用反斜杠字符:“双引号:\”“。 |
4.4.1。字符串插值
除了单引号和三引号字符串之外,任何Groovy表达式都可以在所有字符串文字中进行插值。插值是在对字符串求值时将字符串中的占位符替换为其值的行为。占位符表达式由虚线表达式包围${}
或前缀$
。当GString被传递给通过调用toString()
该表达式将String作为参数的方法时,占位符内的表达式值将被计算为其字符串表示形式。
这里,我们有一个字符串,其中占位符引用了一个局部变量:
def name = 'Guillaume' // a plain string
def greeting = "Hello ${name}"
assert greeting.toString() == 'Hello Guillaume'
但是任何Groovy表达式都是有效的,正如我们在本例中可以看到的算术表达式:
def sum = "The sum of 2 and 3 equals ${2 + 3}"
assert sum.toString() == 'The sum of 2 and 3 equals 5'
不仅在${} 占位符之间允许表达式,而且语句也是如此。但是,声明的价值恰到好处null 。因此,如果在该占位符中插入了多个语句,则最后一个语句应以某种方式返回要插入的有意义值。例如,“1和2的总和等于$ {def a = 1; def b = 2; a + b}”是支持的并且按预期工作但是一个好的做法通常是坚持GString占位符中的简单表达式。 |
除了${}
占位符之外,我们还可以使用$
前缀为虚线表达式的单个符号:
def person = [name: 'Guillaume', age: 36]
assert "$person.name is $person.age years old" == 'Guillaume is 36 years old'
但是,只有形式的虚线表达a.b
,a.b.c
等等,都是有效的,但会包含类似的方法调用,闭包大括号,或算术运算符括号表达式将是无效的。给定以下数字的变量定义:
def number = 3.14
以下语句将抛出一个groovy.lang.MissingPropertyException
因为Groovy认为您正在尝试访问该toString
数字的属性,该属性不存在:
shouldFail(MissingPropertyException) {
println "$number.toString()"
}
您可以"$number.toString()" 将解析器视为解释为"${number.toString}()" 。 |
如果你需要在GString中转义$
或${}
占位符,使它们看起来没有插值,你只需要使用\
反斜杠字符来逃避美元符号:
assert '${name}' == "\${name}"
4.4.2。插值闭包表达式的特例
到目前为止,我们已经看到我们可以在${}
占位符内插入任意表达式,但是闭包表达式有一个特殊的情况和符号。当占位符包含箭头时${→}
,表达式实际上是一个闭包表达式 - 您可以将其视为一个封闭,前面有一个美元:
def sParameterLessClosure = "1 + 2 == ${-> 3}"
assert sParameterLessClosure == '1 + 2 == 3'
def sOneParamClosure = "1 + 2 == ${ w -> w << 3}"
assert sOneParamClosure == '1 + 2 == 3'
闭包是一个无参数的闭包,它不带参数。 | |
这里,闭包只接受一个java.io.StringWriter 参数,你可以使用<< leftShift运算符追加内容。在任何一种情况下,两个占位符都是嵌入式封闭。 |
从外观上看,它看起来像是一种定义要插入的表达式的更冗长的方式,但是闭包与单纯的表达式相比具有一个有趣的优势:懒惰的评估。
让我们考虑以下示例:
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"
我们定义一个number 包含的变量1 ,然后我们在两个GStrings内插入,作为一个表达式eagerGString 和作为闭包lazyGString 。 |
|
我们希望结果字符串包含相同的字符串值1 eagerGString 。 |
|
同样的 lazyGString |
|
然后我们将变量的值更改为新数字 | |
使用普通插值表达式,该值实际上是在创建GString时绑定的。 | |
但是使用闭包表达式时,会在每次将GString强制转换为String时调用闭包,从而生成包含新数字值的更新字符串。 |
带有多个参数的嵌入式闭包表达式将在运行时生成异常。仅允许具有零个或一个参数的闭包。 |
4.4.3。与Java的互操作性
当一个方法(无论是用Java还是Groovy实现)需要a java.lang.String
,但是我们传递一个groovy.lang.GString
实例时,toString()
会自动且透明地调用GString 的方法。
String takeString(String message) {
assert message instanceof String
return message
}
def message = "The message is ${'hello'}"
assert message instanceof GString
def result = takeString(message)
assert result instanceof String
assert result == 'The message is hello'
我们创建一个GString变量 | |
我们仔细检查它是GString的一个实例 | |
然后我们将该GString传递给一个以String作为参数的方法 | |
该takeString() 方法的签名明确表示其唯一参数是String |
|
我们还验证参数确实是String而不是GString。 |
4.4.4。GString和String hashCodes
尽管可以使用插值字符串代替普通Java字符串,但它们以特定方式与字符串不同:它们的hashCodes不同。普通Java字符串是不可变的,而GString的结果字符串表示形式可能会有所不同,具体取决于其内插值。即使对于相同的结果字符串,GStrings和Strings也没有相同的hashCode。
assert "one: ${1}".hashCode() != "one: 1".hashCode()
应该避免使用GString作为Map键的具有不同hashCode值的GString和Strings,尤其是当我们尝试使用String而不是GString检索关联值时。
def key = "a"
def m = ["${key}": "letter ${key}"]
assert m["a"] == null
使用初始对创建映射,其键是GString | |
当我们尝试使用String键获取值时,我们将找不到它,因为Strings和GString具有不同的hashCode值 |
4.5。三重双引号字符串
三重双引号字符串的行为类似于双引号字符串,另外它们是多行的,就像三重单引号字符串一样。
def name = 'Groovy'
def template = """
Dear Mr ${name},
You're the winner of the lottery!
Yours sincerly,
Dave
"""
assert template.toString().contains('Groovy')
双引号和单引号都不需要在三重双引号字符串中进行转义。 |
4.6。Slashy string
除了通常引用的字符串之外,Groovy还提供了/
用作分隔符的字符串。Slashy字符串对于定义正则表达式和模式特别有用,因为不需要转义反斜杠。
一个slashy字符串的示例:
def fooPattern = /.*foo.*/
assert fooPattern == '.*foo.*'
只需使用反斜杠转义正斜杠:
def escapeSlash = /The character \/ is a forward slash/
assert escapeSlash == 'The character / is a forward slash'
Slashy字符串是多行的:
def multilineSlashy = /one
two
three/
assert multilineSlashy.contains('\n')
Slashy字符串也可以插值(即GString):
def color = 'blue'
def interpolatedSlashy = /a ${color} car/
assert interpolatedSlashy == 'a blue car'
有一些问题需要注意。
空的slashy字符串不能用双正斜杠表示,因为Groovy解析器将其理解为行注释。这就是为什么以下断言实际上不会编译,因为它看起来像一个非终止语句:
assert '' == //
由于slashy字符串的设计主要是为了使regexp变得更容易,所以GStrings中的一些错误就像$() slashy字符串一样。 |
4.7。美元邋string的字符串
美元贬值字符串是多行GStrings,以开头$/
和结尾分隔/$
。逃脱的角色是美元符号,它可以逃避另一美元或正斜线。但是美元和正斜线都不需要被转义,除了逃避一个字符串子序列的美元,它会像GString占位符序列一样开始,或者如果你需要转义一个序列,它会像一个结束的美元斜线字符串分隔符一样开始。
这是一个例子:
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 opening dollar slashy
$/$$ escaped closing dollar slashy
/$
assert [
'Guillaume',
'April, 1st',
'$ dollar sign',
'$ escaped dollar sign',
'\\ backslash',
'/ forward slash',
'/ escaped forward slash',
'$/ escaped opening dollar slashy',
'/$ escaped closing dollar slashy'
].every { dollarSlashy.contains(it) }
4.8。字符串摘要表
字符串名称 |
字符串语法 |
内插 |
多行 |
逃避角色 |
单引号 |
|
|
||
三重单引号 |
|
|
||
双引号 |
|
|
||
三倍双引号 |
|
|
||
Slashy |
|
|
||
美元贬值 |
|
|
4.9。人物
与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 类型声明保存字符的变量时显式 |
|
通过与as 操作员使用类型强制 |
|
通过使用强制转换为char操作 |
当字符保存在变量中时, 第一个选项1很有趣,而当必须将char值作为方法调用的参数传递时,其他两个(2和3)更有趣。 |
5.数字
Groovy支持不同类型的整数文字和十进制文字,由通常Number
的Java类型支持。
5.1。积分文字
整数文字类型与Java中相同:
-
byte
-
char
-
short
-
int
-
long
-
java.lang.BigInteger
您可以使用以下声明创建这些类型的整数:
// primitive types
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
5.1.1。替代的非基础10表示
数字也可以用二进制,八进制,十六进制和十进制数表示。
二进制文字
二进制数字以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
5.2。十进制文字
十进制文字类型与Java中的相同:
-
float
-
double
-
java.lang.BigDecimal
您可以使用以下声明创建这些类型的十进制数:
// primitive types
float f = 1.234
double d = 2.345
// infinite precision
BigDecimal bd = 3.456
小数可以使用指数,用e
或E
指数字母,后跟一个可选的标志,以及代表指数一个整数:
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选择java.lang.BigDecimal
十进制数字类型。此外,无论float
和double
支持,但需要一个明确的类型声明,类型强制或后缀。即使BigDecimal
是十进制数的默认值,也可以在方法或闭包中接受这样的文字float
或double
作为参数类型。
十进制数不能使用二进制,八进制或十六进制表示来表示。 |
5.3。文字中的下划线
在编写长字面数字时,更难以弄清楚某些数字是如何组合在一起的,例如成千上万的单词组等。通过允许您在数字文字中放置下划线,更容易发现这些组:
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
5.4。数字类型后缀
我们可以通过给出后缀(见下表)(大写或小写)来强制一个数字(包括二进制,八进制和十六进制)具有特定类型。
类型 | 后缀 |
---|---|
的BigInteger |
|
长 |
|
整数 |
|
BigDecimal的 |
|
双 |
|
浮动 |
|
例子:
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
5.5。数学运算
虽然稍后会介绍运算符,但讨论数学运算的行为以及它们的结果类型是很重要的。
除了分区和功率二进制操作(如下所述),
-
之间的二进制运算
byte
,char
,short
和int
结果int
-
二进制运算涉及
long
用byte
,char
,short
和int
导致long
-
二进制操作涉及
BigInteger
和任何其他整数类型的结果BigInteger
-
涉及二进制运算
BigDecimal
与byte
,char
,short
,int
并BigInteger
导致BigDecimal
-
之间的二进制运算
float
,double
并BigDecimal
导致double
-
两者之间的二进制运算
BigDecimal
导致BigDecimal
下表总结了这些规则:
字节 | 烧焦 | 短 | INT | 长 | 的BigInteger | 浮动 | 双 | BigDecimal的 | |
---|---|---|---|---|---|---|---|---|---|
字节 |
INT |
INT |
INT |
INT |
长 |
的BigInteger |
双 |
双 |
BigDecimal的 |
烧焦 |
INT |
INT |
INT |
长 |
的BigInteger |
双 |
双 |
BigDecimal的 |
|
短 |
INT |
INT |
长 |
的BigInteger |
双 |
双 |
BigDecimal的 |
||
INT |
INT |
长 |
的BigInteger |
双 |
双 |
BigDecimal的 |
|||
长 |
长 |
的BigInteger |
双 |
双 |
BigDecimal的 |
||||
的BigInteger |
的BigInteger |
双 |
双 |
BigDecimal的 |
|||||
浮动 |
双 |
双 |
双 |
||||||
双 |
双 |
双 |
|||||||
BigDecimal的 |
BigDecimal的 |
由于Groovy的操作符重载,通常的算术运算符与工作以及BigInteger 和BigDecimal 在Java中,不像你必须使用显式的方法对这些数字操作。 |
5.5.1。除法运算符的情况
所述除法运算符/
(并/=
用于划分和分配)产生double
结果如果操作数是一float
或double
,和BigDecimal
否则结果(当两个操作数都是一体型的任意组合short
,char
,byte
,int
,long
, BigInteger
或BigDecimal
)。
BigDecimal
分割与所执行的divide()
,如果划分是确切的方法(即,产生了可在相同的精度和分的范围内表示的结果),或使用MathContext
与精度 最大的两个操作数的精度加上一个额外的精度的10和规模 最大的10和最大操作数规模的。
对于像Java中的整数除法,您应该使用该intdiv() 方法,因为Groovy不提供专用的整数除法运算符符号。 |
5.5.2。电力运营商的情况
动力操作由**
操作员表示,有两个参数:基数和指数。功率操作的结果取决于其操作数和操作的结果(特别是如果结果可以表示为整数值)。
Groovy的电源操作使用以下规则来确定结果类型:
-
如果指数是十进制值
-
如果结果可以表示为a
Integer
,则返回一个Integer
-
否则,如果结果可以表示为a
Long
,则返回aLong
-
否则返回一个
Double
-
-
如果指数是一个整数值
-
如果指数严格为负,则返回a
Integer
,Long
或者Double
如果结果值适合该类型 -
如果指数是正数或零
-
如果base是a
BigDecimal
,则返回BigDecimal
结果值 -
如果base是a
BigInteger
,则返回BigInteger
结果值 -
如果base是a
Integer
,则返回a,Integer
如果结果值适合它,否则aBigInteger
-
如果base是a
Long
,则返回a,Long
如果结果值适合它,否则aBigInteger
-
-
我们可以用几个例子说明这些规则:
// 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
6.布尔
7.列表
Groovy使用逗号分隔的值列表(用方括号括起来)来表示列表。Groovy列表是普通的JDK java.util.List
,因为Groovy没有定义自己的集合类。java.util.ArrayList
默认情况下,定义列表文字时使用的具体列表实现,除非您决定另行指定,我们将在后面看到。
def numbers = [1, 2, 3]
assert numbers instanceof List
assert numbers.size() == 3
我们定义一个由逗号分隔的列表编号,并用方括号括起来,然后我们将该列表分配给一个变量 | |
该列表是Java java.util.List 接口的一个实例 |
|
可以使用size() 方法查询列表的大小,并显示我们的列表包含3个元素 |
在上面的示例中,我们使用了同类列表,但您也可以创建包含异构类型值的列表:
def heterogeneous = [1, "a", true]
这里的列表包含数字,字符串和布尔值 |
我们提到默认情况下,列表文字实际上是实例java.util.ArrayList
,但是可以为我们的列表使用不同的支持类型,这要归功于对as
运算符使用类型强制,或者使用变量的显式类型声明:
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
我们使用强制as 操作符来明确请求java.util.LinkedList 实现 |
|
我们可以说保存列表文字的变量是类型的 java.util.LinkedList |
您可以使用[]
下标运算符(用于读取和设置值)访问列表元素,使用正索引或负索引来访问列表末尾的元素以及范围,并使用<<
leftShift运算符将元素附加到一个列表:
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'
assert letters[ 4] == 'e'
assert letters[-1] == 'e'
assert letters[1, 3] == ['b', 'd']
assert letters[2..4] == ['C', 'd', 'e']
访问列表的第一个元素(从零开始计数) | |
使用负索引访问列表的最后一个元素:-1是列表末尾的第一个元素 | |
使用赋值为列表的第三个元素设置新值 | |
使用<< leftShift运算符在列表的末尾追加一个元素 |
|
一次访问两个元素,返回包含这两个元素的新列表 | |
使用范围可以从列表中访问一系列值,从开始到结束元素位置 |
由于列表本质上可以是异构的,因此列表还可以包含其他列表来创建多维列表:
def multi = [[0, 1], [2, 3]]
assert multi[1][0] == 2
定义数字列表列表 | |
访问最顶层列表的第二个元素,以及内部列表的第一个元素 |
8.数组
Groovy重用了数组的列表表示法,但是为了制作这样的文字数组,你需要通过强制或类型声明来明确地定义数组的类型。
String[] arrStr = ['Ananas', 'Banana', 'Kiwi']
assert arrStr instanceof String[]
assert !(arrStr instanceof List)
def numArr = [1, 2, 3] as int[]
assert numArr instanceof int[]
assert numArr.size() == 3
使用显式变量类型声明定义字符串数组 | |
断言我们创建了一个字符串数组 | |
使用as 运算符创建一个int数组 |
|
断言我们创建了一个原始int数组 |
您还可以创建多维数组:
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'
检索数组的第一个元素 | |
将数组的第三个元素的值设置为新值 |
Groovy不支持Java的数组初始化表示法,因为花括号可能会被Groovy闭包的符号误解。 |
9.地图
有时在其他语言中称为字典或关联数组,Groovy具有地图功能。映射将键与值相关联,使用冒号分隔键和值,使用逗号分隔每个键/值对,以及用方括号括起的整个键和值。
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
我们定义了一个字符串颜色名称的映射,与它们的十六进制编码的html颜色相关联 | |
我们使用下标表示法来检查与red 密钥相关的内容 |
|
我们还可以使用属性表示法来断言颜色绿色的十六进制表示 | |
类似地,我们可以使用下标符号来添加新的键/值对 | |
或属性表示法,添加yellow 颜色 |
当使用键的名称时,我们实际上在地图中定义了字符串键。 |
Groovy创建实际上是实例的地图java.util.LinkedHashMap 。 |
如果您尝试访问地图中不存在的密钥:
assert colors.unknown == null
您将检索null
结果。
在上面的示例中,我们使用了字符串键,但您也可以使用其他类型的值作为键:
def numbers = [1: 'one', 2: 'two']
assert numbers[1] == 'one'
在这里,我们使用数字作为键,因为数字可以明确地被识别为数字,因此Groovy不会像我们之前的例子那样创建字符串键。但是考虑一下你要传递一个变量代替键的情况,让该变量的值成为关键:
def key = 'name'
def person = [key: 'Guillaume']
assert !person.containsKey('name')
assert person.containsKey('key')
在key 与相关的'Guillaume' 名称实际上是"key" 字符串,而不是与相关联的值key 变 |
|
地图不包含'name' 密钥 |
|
相反,地图包含一个'key' 键 |
您还可以传递带引号的字符串以及键:[“name”:“Guillaume”]。如果您的密钥字符串不是有效的标识符,那么这是必需的,例如,如果您想创建一个包含哈希的字符串密钥,如:[“street-name”:“Main street”]。 |
当您需要在地图定义中将变量值作为键传递时,必须用括号括起变量或表达式:
person = [(key): 'Guillaume']
assert person.containsKey('name')
assert !person.containsKey('key')
这次,我们key 用括号括起变量,指示解析器传递变量而不是定义字符串键 |
|
地图确实包含name 密钥 |
|
但是地图不像key 以前那样包含密钥 |