1. 程式人生 > >《Groovy語言規範》-語法

《Groovy語言規範》-語法

原文連結  譯者:王山山

語法

本章節涵蓋了Groovy程式語言的語法。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 Groovy文件註釋

與多行註釋相似,Groovy文件註釋也是多行的,但是是以/**開始,以*/結尾。第一個Groovy文件註釋後的行,可以以*開始。這些註釋與以下內容關聯:

  • 型別定義 (類, 介面, 列舉, 註解)
  • 欄位和屬性定義
  • 方法定義

儘管編譯器不會抱怨Groovy文件註釋沒有與上述語言元素關聯,你應該在這些語言元素之前使用註釋加上這些構造。


/**
 * 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文件遵循與Java文件相同的約定。因此你能使用與Java文件一樣的標籤。

1.4.事務行

除單行註釋外,還有一種特殊的行註釋,在Unix系統下經常被稱作是事務行,且允許指令碼直接從命令列執行,前提是你應將安裝了Groovy釋出版,且在PATH中Groovy的命令列是可用的。

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

#字元必須是檔案的第一個字元。任何縮排都會產生編譯錯誤。

2.關鍵字

下面列表表示了Groovy語言所有的關鍵字

          
                          表1.關鍵字
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.引用識別符號

引用識別符號出現在一個點式表示式的點後面。例如,person.name表示式中的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."Simson-${firstname}" = "Homer Simson"

assert map.'Simson-Homer' == "Homer Simson"

4.字串

文字文字以字元鏈的形式表示被稱作字串。Groovy可以讓你例項化java.lang.String物件,也可以例項化GString(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'        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)

4.3.2.Unicode轉義序列

對於那些鍵盤不能表示的字元,你能使用Unicode轉義序列:一個反斜槓,後面跟著‘u’,然後是4個十六進位制的數字。
例如,歐元貨幣標誌可以使用這個表示:

'The Euro currency symbol: \u20AC'

4.4 雙引號字串

雙引號字串是一些列被雙引號包圍的字元:

"a double quoted string"

如果沒有插值表示式,雙引號字串是普通的java.lang.String,如果插值存在則是groocy.lang.GString例項。
為了轉義一個雙引號,你能使用反斜槓字元:”A double quote: \””。

4.4.1 字串插值

任何Groovy表示式可以在所有字元文字進行插值,除了單引號和三單引號字串。插值是使用佔位符上的字串計算值替換佔位符的操作。佔位符表示式是被${}包圍,或字首為$的表示式。當GString被傳遞給一個帶有一個String引數的方法時,佔位符的表示式被計算值,並通過呼叫表示式的toString()方法以字串形式表示。
這裡是一個佔位符引用區域性變數的字串:

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。如果有多個語句插入佔位符,那麼最後一個語句應該返回一個有意義的值被插入佔位符。例如,”The sum of 1 and 2 is equal to ${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}" (1)
assert sParameterLessClosure == '1 + 2 == 3'

def sOneParamClosure = "1 + 2 == ${ w -> w << 3}" (2)
assert sOneParamClosure == '1 + 2 == 3'

(1)這是一個不攜帶引數的無參閉包
(2)這裡的閉包攜帶一個java.io.StringWrite引數,你能使用<<追加內容。在這兩處,佔位符被嵌入閉包。

在外觀上,定義一個表示式被插入看著有一些冗長,但閉包相比表示式有一個有趣的優勢:延遲計算。
讓我們思考如下示例:

def number = 1 (1)
def eagerGString = "value == ${number}"
def lazyGString = "value == ${ -> number }"

assert eagerGString == "value == 1" (2)
assert lazyGString == "value == 1" (3)

number = 2 (4)
assert eagerGString == "value == 1" (5)
assert lazyGString == "value == 2" (6)

(1)我們定義一個包含1的number變數,然後插入兩個GString之中,在eagerGString中作為表示式,在lazyGString中作為閉包。
(2)我們期望對於eagerGString得到的結果字串是包含1的相同字串
(3)lazyGString相似
(4)然後給變數賦一個新值
(5)使用純插值表示式,這值在GString建立時結合
(6)但使用閉包表示式,GString被強轉為Sring時,閉包被呼叫,併產生包含新數值的更新字串。
一個嵌入的閉包表示式,攜帶超過一個引數,那麼在執行時將會產生一個異常。閉包僅僅允許攜帶0個或1個引數。

4.4.3.與Java的互操作性

當一個方法(無論是在Java或是Groovy中實現)預期需要一個java.lang.String,而我們傳遞了一個groovy.lang.GString例項,GString的toString()方法將會被自動透明的呼叫。


String takeString(String message) { (4)
  assert message instanceof String (5)
  return message
}

def message = "The message is ${'hello'}" (1)
assert message instanceof GString (2)

def result = takeString(message) (3)
assert result instanceof String
assert result == 'The message is hello'

(1)我們建立一個GSring變數
(2)我們仔細檢查GString例項
(3)我們將GString傳遞個一個攜帶String引數的方法
(4)takeString()明確說明它的唯一引數是一個String
(5)我們也驗證一個引數是String而不是GString

4.4.4.GString和String的hashCode

雖然插值字串可以代替普通Java字串,它們用一種不同的方式是字串不同:它們的hashCode是不同的。普通Java字串是不可變的,而一個GString依賴於插入的值,它的String是可變的。即使有相同的字串結果,GString和String也沒有相同的hashCode。

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

GString和String有不同的hashCode值,應該避免使用GSring作為Map的鍵值,我們使用String代替GString取出一個關聯值。

def key = "a"
def m = ["${key}": "letter ${key}"] (1)

assert m["a"] == null (2)

(1)map被一個初始化鍵值對建立,其鍵值是GString
(2)當我們嘗試使用String鍵值獲取值時,我們並沒獲取對應值,因為String和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.斜槓字串

除了通常的帶引號字串,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')

斜槓字串也能被插值(如,GString):

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

assert interpolatedSlashy == 'a blue car'

有幾個陷阱需要注意:
一個空的斜槓字串不能使用雙正斜槓表示,因為它被Groovy解析器作為一個單行註釋理解。這就是為什麼以下斷言實際上無法編譯,因為它看起來像一個無終止的語句:

assert '' == //

4.7.美元符修飾的斜槓字串

美元符斜槓字串是一個有開口$/和閉口$/界定的多行GString。這轉義字元是美元符,它能轉義另一個美元符,或一個正斜槓。但是雙美元符和雙正斜槓不用被轉義,除了轉義像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 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) }

4.8.字串總結表

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

4.9.字元

與Java不同,Groovy沒有顯式的字元文字,然而你可以通過三種不同方式,可以將Groovy字串實際作為一個字元使用。

char c1 = 'A' (1)
assert c1 instanceof Character

def c2 = 'B' as char (2)
assert c2 instanceof Character

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

(1)當定義變數時,通過指定char型別,使變數包含字元
(2)通過as操作符使用型別強制轉換
(3)通過char操作符做型別轉換
第一個選項是(1)有趣的當一個字元在一個變數中,而另外兩個(2和3)是更令人關注時char值必須作為一個方法呼叫的引數。

5.數字

Groovy支援不同型別的整數和小數,通常以Java的Number型別返回。

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.可選擇的非十進位制表示

二進位制數
在Java6及以前和Groovy一樣,數字只能使用十進位制,八進位制和十六進位制表示,使用Java7和Groovy2你能使用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.數字型別字尾

通過使用大寫或小寫型別字尾(見下表),我們能強制將一個數字(包括二進位制,八進位制,十六進位制)給一個指定型別。

Type            Suffix
BigInteger      G 或 g
Long            L 或 l
Integer         I 或 i
BigDecimal      G 或 g
Double          D 或 d
Float           F 或 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

5.5.數學運算

儘管運算在以後會被覆蓋,討論數學運算和結果型別仍然是重要的。
除法與冪方二元操作符放在一邊(下文討論)。

  • byte char short和int進行二元操作的結果是int
  • 使用long與byte char short int進行二元操作的結果是long
  • 使用BigInteger與任意其他整數型別進行二元操作結果是BigInteger
  • float double與BigDecimal進行二元運算的結果是double
  • 兩個BigDecimal進行二元運算的結果是BigDecimal

下表總結了這些原則:

        byte    char    short    int    long    BigInteger    float      double    BigDecimal
byte    int     int     int      int    long    BigInteger    double     double    double
char            int     int      int    long    BigInteger    double     double    double
short                   int      int    long    BigInteger    double     double    double
int                              int    long    BigInteger    double     double    double
long                                    long    BigInteger    double     double    double
BigInteger                                      BigInteger    double     double    double
float                                                         double     double    double
double                                                                   double    double
BigDecimal                                                                         BigDecimal

由於Groovy操作符過載,BigInteger與BigDecimal通常也能進行運算操作,與Java不同,在Java中你不得不顯式使用方法操作這些數字。

5.5.1除法運算子的情況

如果任何一個運算元是float或double,那麼除法運算子/(和/= 用於除法和賦值)產生double結果,否則(當兩個運算元是一個與整型型別short, char, byte, int, long, BigInteger or BigDecimal的任意組合)是一個BigDecimal結果。

如果除法是精確的(如,結果可以在相同的精度和標度範圍內精確表示),那麼BigDecimal的除法實際執行的是divide()方法,或使用兩個運算元的最大精度加10,和一個最大值為10的標度的MathContext。

對於整數除法和Java相同,你應該使用intdiv()方法,因為Groovy沒有專門提供一個整數操作符。

5.5.2.冪運算情況

冪運算操作符使用**操作符,有兩個引數:基數和指數。冪運算的結果取決於它的運算元以及操作的結果(特別是結果可以被表示為一個整數值)。

以下這些原則被用於決定Groovy冪運算操作結果的型別:

(1)如果指數是一個小數
	1.如果結果能作為一個Integer表示,那麼返回一個Integer
	2..如果結果能作為一個Long表示,那麼返回一個Long
	3.否則返回一個Double

(2)如果指數是一個整數
 	1.如果是一個嚴格的負數,那麼返回一個Integer,Long或Double,結果值使用那種型別填充。
        2.如果指數是正數或0
 	  1)如果基數是BigDecimal,那麼返回一個BigDecimal結果值
	  2)如果基數是BigInteger,那麼返回一個BigInteger結果值
	  3)如果基數是Integer,那麼返回一個Integer值,否則返回BigInteger
	  4)如果基數是Long,那麼返回一個Long值,否則返回BigInteger

我們使用一些例項說明這些原則:

// 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.布林

Boolean是一種特殊的資料型別,用於表示真值:true和false。使用這種資料型別作為跟蹤真假條件的簡單標誌。
Boolean能被儲存在變數中,成員變數中,就像其他資料型別一樣:

def myBooleanVariable = true
boolean untypedBooleanVar = false
booleanField = true

true和false是僅有的兩個原始布林值。但更復雜的布林表示式能使用邏輯操作符表示。
除此之外,Groovy有一些特殊的規則(經常因為Groovy真值涉及)用於將非布林值物件轉化為一個布林值。

7.列表

Groovy使用逗號分隔列表中的值,並使用方括號包圍,用來指定一個列表。Groovy的列表是java.util.List,因為Groovy沒有定義任何集合類。當定義一個列表常量時,預設的列表具體實現是java.util.ArrayList,除非你指定,我們將在後面看到。

def numbers = [1, 2, 3] (1)

assert numbers instanceof List (2)
assert numbers.size() == 3 (3)

(1)我們定義用逗號分隔,並用方括號包圍列表數字,並將列表賦值給一個變數
(2)list是java java.util.List介面的例項
(3)列表的大小可以使用size()方法查詢,表明列表有三個元素

在上面的示例中,我們使用了一個元素型別相同的列表,我們也能建立包含不同型別元素的列表:

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

(1)我們的列表包含一個數字,一個字串,一個布林值

我們提及到,預設的列表字面量實際是java.util.ArrayList的例項,但列表使用不同返回型別也是可以的,使用as操作符進行型別轉換,或使用變數的定義型別:

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

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

LinkedList otherLinked = [3, 4, 5] (2)
assert otherLinked instanceof java.util.LinkedList

(1)我們使用as操作符進行型別轉換,顯式請求一個java.util.LinkedList實現
(2)我們使用型別為java.util.LinkedList的變數儲存列表字面量

你能通過下標操作符[](讀和寫元素值)並使用正索引值訪問列表元素或負索引值從列表尾部訪問元素,也可以使用範圍,或使用左移<<追加列表元素:

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

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

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

letters[2] = 'C' (3)
assert letters[2] == 'C'

letters << 'e' (4)
assert letters[ 4] == 'e'
assert letters[-1] == 'e'

assert letters[1, 3] == ['b', 'd'] (5)
assert letters[2..4] == ['C', 'd', 'e'] (6)

(1)訪問列表的第一個元素(索引從零開始計算)
(2)使用負索引訪問列表的最後一個元素:-1是列表從尾部開始的第一個元素
(3)使用賦值操作為列表的第三個元素設定一個新值
(4)使用<<左移操作符在列表尾部追加一個元素
(5)一次訪問兩個元素,並返回一個包含這兩個元素的新列表
(6)使用範圍訪問列表中這個範圍內的元素,從start到end元素位置

因為列表可以很自然的做到元素型別不同,因此列表也可以包含列表用於建立多維列表:

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

(1)定義一個數字列表的列表
(2)訪問頂級列表的第二個元素,內部列表的第一個元素

8.陣列

Groovy使用列表標記來標記陣列,但為了建立字面量陣列,你需要通過型別轉換或型別定義來定義陣列型別。

String[] arrStr = ['Ananas', 'Banana', 'Kiwi'] (1)

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

def numArr = [1, 2, 3] as int[] (3)

assert numArr instanceof int[] (4)
assert numArr.size() == 3

(1)使用顯式變數型別定義一個字串陣列
(2)斷言說明我們建立了一個字串陣列
(3)使用as操作符建立以int陣列
(4)斷言表明我們建立了一個原始型別的int陣列

你也能建立多維陣列:

def matrix3 = new Integer[3][3] (1)
assert matrix3.size() == 3

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

(1)你能定義一個新陣列的邊界
(2)或不指定它的邊界定義一個新陣列

通過與列表相同的標記訪問陣列的元素:

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

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

(1)取得陣列的第一個元素
(2)為陣列的第三個元素設定一個新值

Java陣列初始化標記Groovy不支援,因為大括號會被誤解為Groovy的閉包標記。

9.對映

在其它語言中,有時候稱為字典或關聯陣列,Groovy稱為對映。對映使鍵到值關聯,使用冒號將鍵值分隔開,每個鍵值對使用逗號,整個鍵和值使用方括號包圍。

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

assert colors['red'] == '#FF0000' (2)
assert colors.green == '#00FF00' (3)

colors['pink'] = '#FF00FF' (4)
colors.yellow = '#FFFF00' (5)

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

assert colors instanceof java.util.LinkedHashMap

(1)我們定義了一個字串顏色名關聯十六進位制的html顏色的對映
(2)我們使用下標標記檢查red鍵值關聯的內容
(3)我們也能使用屬性標記訪問綠顏色十六進位制表示式
(4)相似的,我們也能使用下標標記新增一個新的鍵值對
(5)或者使用屬性標記新增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'] (1)

assert !person.containsKey('name') (2)
assert person.containsKey('key') (3)

(1)key同’Guillaume’關聯,名字將會變為”key”字串,而不是其值
(2)這個對映不包括”name”鍵
(3)代替的是,對映包括一個”key”鍵

你也能通過引用的字串以及鍵: [“name”: “Guillaume”]。如果你的見字串不是一個合法的識別符號,這是強制的,例如,如果你想建立一個字串鍵像雜湊:

["street-name": "Main street"]。

在對映定義中需要傳遞變數值,你必須使用圓括號包圍這個變數或表示式:

person = [(key): 'Guillaume'] (1)

assert person.containsKey('name') (2)
assert !person.containsKey('key') (3)

(1)這次,我們使用圓括號包圍key變數,指示解析器我們傳遞一個變數,而不是定義一個字串鍵
(2)對映包含name鍵
(3)但對映不像之前包含key鍵