Groovy 语言文档v3.0.3 翻译版

Groovy 语言文档v3.0.3

1.语言规范

1.1 句法

1.1.1 注释

单行注释
// 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 */
类,接口,方法注释

其实与java的注释都差不多了,写代码笔者建议多写写注释

/**
 * 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}"
    }
}

与java 不一样的是,groovy支持运行时的doc,意识就是在运行时,可以通过代码获取到类的注释文本

默认情况,Groovydoc是禁用的,如果你想要开启,可以通过jvm选项启动

-Dgroovy.attach.runtime.groovydoc=true

笔者是觉得这个特性目前是没有太大用处,但是作为一种新特性,肯定是有一点点用处,说不定以后注释就和注解一样,有着特定的意义与功能,代码如下。

/**@
 * Some class groovydoc for Foo
 */
class Foo {
    /**@
     * Some method groovydoc for bar
     */
    void bar() {
    }
}

assert Foo.class.groovydoc.content.contains('Some class groovydoc for Foo') 
assert Foo.class.getMethod('bar', new Class[0]).groovydoc.content.contains('Some method groovydoc for bar')

1.1.2 关键字

asassertbreakcase
catchclassconstcontinue
defdefaultdoelse
enumextendsfalsefinally
forgotoifimplements
importininstanceofinterface
newnullpackagereturn
superswitchthisthrow
throwstraittruetry
varwhile

1.1.3 变量命名规则

普通命名规则

其实这一块对于java程序员可以不用说了,粗略讲一下,变量名以字母,$,或者_开头,但绝对不能是数字开头。

字符可以在如下的范围

  • “ a”到“ z”(小写的ascii字母)
  • “ A”到“ Z”(大写字母)
  • ‘\ u00C0’至’\ u00D6’
  • ‘\ u00D8’至’\ u00F6’
  • ‘\ u00F8’至’\ u00FF’
  • ‘\ u0100’到’\ uFFFE’

然后后面的字符可以包含字母和数字

def name
def item3
def with_underscore
def $dollarStart

还有一个就是变量名不能是关键字

带引号的的变量名

带引号的变量名一般是在点号运算符后面,例如person.name可以使用person."name"来进行表示,这样使得groovy 的变量命名变得很灵活

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

map."an identifier with a space and double quotes" = "ALLOWED"//往map从存放键值对,相当于java的put
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 的GString(插值字符串),同样是可以被支持的,如下定义了一个firstname的变量,然后,在GString字符串中使用插值表达式进行引用

def firstname = "Homer"
map."Simpson-${firstname}" = "Homer Simpson"

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

1.1.4 字符串

在groovy中,有2中字符串基础类型,一种是java中的java.lang.String对象字符串,另外一种是groovy中支持的GStringsgroovy.lang.GString,GStrings在其他语言中也被成为内插值字符串。

单引号字符串
'a single-quoted string'

注意:单引号字符串是纯文本java.lang.String类型,不支持插值。

字符串拼接
'a' + 'b'
三引号字符串
'''a triple-single-quoted string'''

注意:单引号字符串是纯文本java.lang.String类型,不支持插值。

三重单引号字符串可能跨域多行,字符串内容可以跨界,无需分成几段,无需类似java中使用+进行拼接以及换行符号

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')
特殊字符串的转义
转义序列字符
\b退格键
\F换页
\n换行
\r回车
\s单空格
\t制表
\\反斜杠
\’单引号字符串中的单引号(对于三重单引号和双引号字符串是可选的)
\"双引号字符串中的双引号(三重双引号和单引号字符串是可选的)
Unicode转义

没什么可说的,看代码

'你好的Unicode是: \u4F60\u597D'
双引号字符串
"a double-quoted string"
字符串插值

除了单引号,与三引号单引号字符串外,任何Groovy 表达式都是支持插值的

def name = 'Guillaume' // a plain string
def greeting = "Hello ${name}"

插值表达式不仅仅支持变量引用,还支持数学运算呢

def sum = "The sum of 2 and 3 equals ${2 + 3}"

除了${}占位符,我们还可以用$,而不需要花括号当然局限性就打了很多,只限在变量引用的范围。如下代码,

def name="UsErNaMeIs:CaT"
println "$name"
println "$name.toUpperCase()"//需要注意这个语句会抛出异常,因为解析器将其解释为"${name.toUpperCase}()",所以会抛出属性未找到异常。使用的时候建议谨慎使用

如下,如果表达式不明确,会出现.误引用的问题。所以一般建议是用${}

String bookName="红楼梦"

String desc="这本书的名字是:$bookName.作者是:xxx"
String desc2="这本书的名字是:${bookName}.作者是:xxx"
内插值闭包的情况

由于内插值其实相当一个返回特定数据的代码块,所以,其必也支持Groovy 的伟大特性之一闭包

def sParameterLessClosure = "1 + 2 == ${-> 3}" //不带参数的闭包
assert sParameterLessClosure == '1 + 2 == 3'

def sOneParamClosure = "1 + 2 == ${ w -> w << 3}" //使用<<3为w赋值,这里的<<3不是左移运算符哦
assert sOneParamClosure == '1 + 2 == 3'

内插值闭包提供了一个有趣的特点,惰性求值

def number = 1
def eagerGString = "value == ${number}"
def lazyGString = "value == ${ -> number }"
println eagerGString
println lazyGString
number=2
println eagerGString     //输出还是1,字符串在编译的时候,就已经定型了
println lazyGString     //输出的是2,也就是说,字符串的值只会在执行到的时候,才会去计算
与Java的相互转换

如果有一个方法,其输入参数是java.lang.String时候,但是我们传递了一groovy.lang.GString的话,那么groovy 将会透明的帮我们调用toString()方法

//定义一个方法,要求输入的参数时java.lang.String类型
String takeString(String message) {         
    assert message instanceof String        
    return message
}

//定义一个GString类型的字符串
def message = "The message is ${'hello'}"   
assert message instanceof GString      //断言以证明其实GString类型     

def result = takeString(message)  		//将GString 作为参数传递给taskString方法,发现其没有发生异常          
assert result instanceof String
GString和String 的 哈希值

纯java字符串的hashCode是不可变的,而GString的字符串表示却有所不同,取决于其内插值,即使对于相同的GString和String ,其HashCode 也可能不同,如下例子,虽然都是一样的字符串,但是HashCode 是不同的

println "one:${1}".hashCode()
println "one:1".hashCode()

//但是,如果对GString进行toString()的话,就可以得到一样的哈希值
println "one:${1}".toString().hashCode()
println "one:1".hashCode()

由于GString的hash是不可预测的,所以,在使用map 存放key为String的数据的时候,应该尽量避免使用GString,因为对于map,其取数据时用hash值进行获取的,这一点是需要开发过程中特别注意的点。

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

assert m["a"] == null    //可以断言发现是取不到值的  
三双引号字符串

特点,多行,跟普通的字符串无差别

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

    You're the winner of the lottery!

    Yours sincerly,

    Dave
"""
斜线字符串
//正常使用方法
def fooPattern = /.*foo.*/
///转义方法
def escapeSlash = /The character \/ is a forward slash/
//多行用法
def multilineSlashy = /one
    two
    three/
//补位方法
def color = 'blue'
def interpolatedSlashy = /a ${color} car/
//特例    
assert '' == //

$字符串
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
/$
字符串汇总
字符
char c1 = 'A' //使用char承接
assert c1 instanceof Character

def c2 = 'B' as char //强制转换
assert c2 instanceof Character

def c3 = (char)'C' //强转
assert c3 instanceof Character

1.1.5 数字

// 基本类型
byte  b = 1
char  c = 2
short s = 3
int   i = 4
long  l = 5

//高精度
BigInteger bi =  6

//自动类型识别
def a = 1
assert a instanceof Integer

//如果是使用def 关键字进行变量的承接
// 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
二进制

使用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


八进制
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
十六进制
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支持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
下划线语法

从上面的例子就可以看到有下划线的数字了。下划线仅仅为了方便人眼观看方便而为之

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

数字类型后缀

在java中,通常可以使用8l类似l的后缀的代码来表示Long类型数字,Groovy中也可以的

类型后缀
BigIntegerG or g
LongL or l
IntegerI or i
BigDecimalG or g
DoubleD or d
FloatF or f
assert 42I == new Integer('42')
assert 42i == new Integer('42') // 小写字母可能更有利与读写 
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
数学运算符
bytecharshortintlongBigIntegerfloatdoubleBigDecimal
byteintintintintlongBigIntegerdoubledoubleBigDecimal
charintintintlongBigIntegerdoubledoubleBigDecimal
shortintintlongBigIntegerdoubledoubleBigDecimal
intintlongBigIntegerdoubledoubleBigDecimal
longlongBigIntegerdoubledoubleBigDecimal
BigIntegerBigIntegerdoubledoubleBigDecimal
floatdoubledoubledouble
doubledoubledouble
BigDecimalBigDecimal
// 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

1.1.6 布尔类型

def myBooleanVariable = true
boolean untypedBooleanVar = false
booleanField = true

1.1.7 列表类型

def numbers = [1, 2, 3]         

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


//另外,也支持把不同类型的数据放置到一个列表中。
def heterogeneous = [1, "a", true]  

//默认是采用ArrayList作为存储的容器的。
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

数组中的元素如何获取

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.1.8 数组

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

1.1.9 Map类型

//因为{}花括号已经被闭包所占用了,所以此处使用的是数组进行包括起来进行map的初始化
def colors = [red: '#FF0000', green: '#00FF00', blue: '#0000FF']   

//获取map中元素的方法
assert colors['red'] == '#FF0000'    
assert colors.green  == '#00FF00'    

//设置map中对应key value
colors['pink'] = '#FF00FF'           
colors.yellow  = '#FFFF00'           

assert colors.pink == '#FF00FF'
assert colors['yellow'] == '#FFFF00'
//默认是采用LinkedHashMap类型的方式进行初始化
assert colors instanceof java.util.LinkedHashMap

//如果尝试获取一个不存在的key,将会得到一个null
assert colors.unknown == null


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

assert !person.containsKey('name')   //使用name进行获取是得不到的
assert person.containsKey('key')     //只能使用key进行获取,所以由此可以看出map的key不支持变量方式

//那么如果我们想要使用变量作为map 的key的时候,应该怎么做?只需要在变量名左右加上括号即可
person = [(key): 'Guillaume']        


1.2 运算符

1.2.1 数学运算符

一元运算符
assert +3 == 3
assert -4 == 0 - 4

assert -(-1) == 1  
#### 赋值运算符
  • +=
  • -=
  • *=
  • /=
  • %=
  • **=
def a = 4
a += 3

assert a == 7

def b = 5
b -= 3

assert b == 2

def c = 5
c *= 3

assert c == 15

def d = 10
d /= 2

assert d == 5

def e = 10
e %= 3

assert e == 1

def f = 3
f **= 2

assert f == 9

1.2.2 关系运算符

OperatorPurpose
==equal
!=不同
<小于
<=小于等于
>大于
>=大于等于
===强等于(Since Groovy 3.0.0)
!==强不等于

===!==都是通过调用对象的is()方法来进行甄别的

import groovy.transform.EqualsAndHashCode

@EqualsAndHashCode
class Creature { String type }

def cat = new Creature(type: 'cat')
def copyCat = cat
def lion = new Creature(type: 'cat')

assert cat.equals(lion) // java自带的判别方法
assert cat == lion      // Grovyy短判别方法

assert cat.is(copyCat)  // Groovy视为相等
assert cat === copyCat  // 两者是相等的
assert cat !== lion     // 两者是不相等的

1.2.3逻辑运算符

  • &&: logical “and”
  • ||: logical “or”
  • !: logical “not”

1.2.4位运算符

  • &: bitwise “and”
  • |: bitwise “or”
  • ^: bitwise “xor” (exclusive “or”)
  • ~: bitwise negation

1.2.5 条件运算符

非运算符
assert (!true)    == false                      
assert (!'foo')   == false                      
assert (!'')      == true  
三元运算符
result = (string!=null && string.length()>0) ? 'Found' : 'Not found'
猫王运算符
displayName = user.name ? user.name : 'Anonymous'   
displayName = user.name ?: 'Anonymous'              //上面的例子等价于下面

import groovy.transform.ToString

@ToString
class Element {
    String name
    int atomicNumber
}

def he = new Element(name: 'Helium')
he.with {
    name = name ?: 'Hydrogen'   //这个形式可以使用下面的形式进行替换
    atomicNumber ?= 2           // 不存在的时候进行赋值,存在则不进行赋值,效果与上面的一样
}
assert he.toString() == 'Element(Helium, 2)'

1.2.6 对象运算符

安全定位符

java中,我们场景因为你对象是空的导致的调用方法引发NPE,所以在使用之前经常要判断对象是否为null,groovy中可以使用?.来进行对象属性的的调用,即使属性为null也不至于报错。

def person = Person.find { it.id == 123 }    
def name = person?.name                      
assert name == null  
直接属性操作
class User {
    public final String name                 
    User(String name) { this.name = name}
    String getName() { "Name: $name" }       
}
def user = new User('Bob')
assert user.name == 'Name: Bob' //看似获取的只是属性值,其实Groovy已经偷偷的帮我们调用了getName()方法
//那么如果我们不希望Groovy 帮我们自动调用方法的时候,应该怎么办呢?答案是通过@进行调用即可
assert user.@name == 'Bob'       //.@强制使用field而非getter            
方法指针运算符

.&可以用于把方法存储于变量之中

def str = 'example of method reference'            
def fun = str.&toUpperCase        //得到str对象的toUpperCase方法的引用                 
def upper = fun()                 //调用方法,这里有点神奇哈。直接调用方法而无需送入对象,是不是对象与方法已经绑定了呢?                 
assert upper == str.toUpperCase()   

方法指针存储者诸多优点,第一方法指针的存储类型实际上是groovy.lang.Closure,所以他可以在任何一个使用闭包的地方进行使用

//transform方法对list中每一个元素都进行action方法的调用,并且将转化后的类型以列表的方式返回
def transform(List elements, Closure action) {                    
    def result = []
    elements.each {
        result << action(it)
    }
    result
}
//我们定义一个用于吧person对象转成字符串的方法
String describe(Person p) {                                       
    "$p.name is $p.age"
}
//取得方法的引用
def action = this.&describe                                       
def list = [
    new Person(name: 'Bob',   age: 42),
    new Person(name: 'Julia', age: 35)]   
//使用
assert transform(list, action) == ['Bob is 42', 'Julia is 35'] 

//使用.&new可得到一个对象的构造函数
def foo  = BigInteger.&new
def fortyTwo = foo('42')
assert fortyTwo == 42G

//可以得到class的方法引用,但使用的时候,第一个参数要被调用的对象传入,可以视为this对象
def instanceMethod = String.&toUpperCase
assert instanceMethod('foo') == 'FOO'
方法引用运算符

groovy同样支持java8提供的::运算符

import groovy.transform.CompileStatic
import static java.util.stream.Collectors.toList

@CompileStatic
void methodRefs() {
    assert 6G == [1G, 2G, 3G].stream().reduce(0G, BigInteger::add)                           

    assert [4G, 5G, 6G] == [1G, 2G, 3G].stream().map(3G::add).collect(toList())              

    assert [1G, 2G, 3G] == [1L, 2L, 3L].stream().map(BigInteger::valueOf).collect(toList())  

    assert [1G, 2G, 3G] == [1L, 2L, 3L].stream().map(3G::valueOf).collect(toList())          
}

1.2.7 正则运算符

模式运算符
def p = ~/foo/
assert p instanceof Pattern//得到的是一个Pattern实例对象

p = ~'foo'                                                        
p = ~"foo"                                                        
p = ~$/dollar/slashy $ string/$                                   
p = ~"${pattern}"          
查询运算符
//可以使用功能=~来进行正则的匹配
def text = "some text to match"
def m = text =~ /match/                                           
assert m instanceof Matcher                                       
if (!m) {                                                         
    throw new RuntimeException("Oops, text not found!")
}
匹配运算符
//使用==~来得到一个是否匹配的结果
m = text ==~ /match/                                              
assert m instanceof Boolean                                       
if (m) {                                                          
    throw new RuntimeException("Should not reach that point!")
}

1.2.8 其他运算符

解构运算符
class Car {
    String make
    String model
}
def cars = [
       new Car(make: 'Peugeot', model: '508'),
       new Car(make: 'Renault', model: 'Clio')]       
def makes = cars*.make 
//cars.stream().map({it.make}).collect(Collectors.toList())                                
assert makes == ['Peugeot', 'Renault']  

//
class Component {
    Long id
    String name
}
class CompositeObject implements Iterable<Component> {
    def components = [
        new Component(id: 1, name: 'Foo'),
        new Component(id: 2, name: 'Bar')]
		
    @Override
    Iterator<Component> iterator() {
        components.iterator()
    }
}
def composite = new CompositeObject()
assert composite*.id == [1,2]//可见*.的原理为对象必须是继承自Iterable,可迭代的。
assert composite*.name == ['Foo','Bar']

//#########################################################################


class Make {
    String name
    List<Model> models
}

//支持多级调用
@Canonical
class Model {
    String name
}

def cars = [
    new Make(name: 'Peugeot',
             models: [new Model('408'), new Model('508')]),
    new Make(name: 'Renault',
             models: [new Model('Clio'), new Model('Captur')])
]

def makes = cars*.name
assert makes == ['Peugeot', 'Renault']

def models = cars*.models*.name//多级调用
assert models == [['408', '508'], ['Clio', 'Captur']]
assert models.sum() == ['408', '508', 'Clio', 'Captur'] // flatten one level
assert models.flatten() == ['408', '508', 'Clio', 'Captur'] // flatten all levels (one in this case)

//#########################################################################
class Car {
    String make
    String model
}
def cars = [
   [
       new Car(make: 'Peugeot', model: '408'),
       new Car(make: 'Peugeot', model: '508')
   ], [
       new Car(make: 'Renault', model: 'Clio'),
       new Car(make: 'Renault', model: 'Captur')
   ]
]
def models = cars.collectNested{ it.model }//collectNested语法为*.的简写方式
assert models == [['408', '508'], ['Clio', 'Captur']]
解构方法运算符
int function(int x, int y, int z) {
    x*y+z
}
def args = [4,5,6]
assert function(*args) == 26//*有类似于python的解构的功能,把数组中对象按照顺序作为方法的参数传入


解构列表运算符
def items = [4,5]                      
def list = [1,2,3,*items,6]           //可以直接用来解构列表 
assert list == [1,2,3,4,5,6]    
解构Map元素
def m1 = [c:3, d:4]                   
def map = [a:1, b:2, *:m1]       //直接将m1解构,并且存入map中     
assert map == [a:1, b:2, c:3, d:4]

def m1 = [c:3, d:4]        //解构的时候,遇到同样key的值,以顺序前后进行放置,后者覆盖前者           
def map = [a:1, b:2, *:m1, d: 8]      
assert map == [a:1, b:2, c:3, d:8]  
范围运算符
//类似于python ,但是又不需要多余的两个点号。
def range = 0..5                                    
assert (0..5).collect() == [0, 1, 2, 3, 4, 5]				//给定范围,含头,含尾       
assert (0..<5).collect() == [0, 1, 2, 3, 4]         //给定范围,含头不含尾
assert (0..5) instanceof List        								//默认初始类型为List实际为(groovy.lang.Range)            
assert (0..5).size() == 6            

Range的实现是非常轻量级,因为它只存储一个上界与一个下界,可以从一个实现了Comparable接口的对象,并且实现next()previous()方法,以得到上一个或者下一个元素的实际值,例如,可以创建一个字符Range

assert ('a'..'d').collect() == ['a','b','c','d']
太空船运算符?

<=>是compareTo的委托方法

assert (1 <=> 1) == 0				//1==1 	返回0
assert (1 <=> 2) == -1			//1<2		返回-1
assert (2 <=> 1) == 1				//2>1 	返回1
assert ('a' <=> 'z') == -1	//'a'<'z'
下标运算符

下标运算符是getAt 跟putAt方法的缩写

def list = [0,1,2,3,4]
assert list[2] == 2                         
list[2] = 4                                 
assert list[0..2] == [0,1,4]                
list[0..2] = [6,6,6]                        
assert list == [6,6,6,3,4] 

可以定义一个类,该类实现getAt与putAt方法,则可以通过对象[index]下标对元素进行获取

class User {
    Long id
    String name
    def getAt(int i) {                                             
        switch (i) {
            case 0: return id
            case 1: return name
        }
        throw new IllegalArgumentException("No such element $i")
    }
    void putAt(int i, def value) {                                 
        switch (i) {
            case 0: id = value; return
            case 1: name = value; return
        }
        throw new IllegalArgumentException("No such element $i")
    }
}
def user = new User(id: 1, name: 'Alex')                           
assert user[0] == 1                                                
assert user[1] == 'Alex'                                           
user[1] = 'Bob'                                                    
assert user.name == 'Bob'                                          

同样的,所给下标如果无法获取到元素,也就是对象是null,就会导致异常的出现,提供了下面

安全下标运算符

类似于?.同样的也有?[],可以避免异常

String[] array = ['a', 'b']
assert 'b' == array?[1]      // 获取方法
array?[1] = 'c'              // 设置方法
assert 'c' == array?[1]

array = null
assert null == array?[1]     // 返回null
array?[1] = 'c'              // 放弃设置值
assert null == array?[1]

def personInfo = [name: 'Daniel.Sun', location: 'Shanghai']
assert 'Daniel.Sun' == personInfo?['name']      // 获取map值
personInfo?['name'] = 'sunlan'                  // 设置map值
assert 'sunlan' == personInfo?['name']

personInfo = null
assert null == personInfo?['name']              // 返回null
personInfo?['name'] = 'sunlan'                  // 忽略错误
assert null == personInfo?['name']
成员判断运算符
def list = ['Grace','Rob','Emmy']
assert ('Emmy' in list)       
//等同于调用 `list.contains('Emmy')` 或者 `list.isCase('Emmy')`
等同运算符
def list1 = ['Groovy 1.8','Groovy 2.0','Groovy 2.3']        
def list2 = ['Groovy 1.8','Groovy 2.0','Groovy 2.3']        
assert list1 == list2     //仅仅判断数值,返回true                                  
assert !list1.is(list2)   //判断引用,故而返回false
强转
Integer x = 123
String s = (String) x //强转本质也可以看做调用当前对象的asType方法
//可以使用如下方法,来吧对象转化成为一个完全与之无任何关系的对象
class Identifiable {
    String name
}
class User {
    Long id
    String name
    def asType(Class target) {                                              
        if (target == Identifiable) {
            return new Identifiable(name: name)
        }
        throw new ClassCastException("User cannot be coerced into $target")
    }
}
def u = new User(name: 'Xavier')                                            
def p = u as Identifiable  //强转为异父异母的对象                                                 
assert p instanceof Identifiable   //强转后的对象成为了  Identifiable                                     
assert !(p instanceof User)    			//并且已经不再是User。因为它在asType方法new出来的
泛型运算符
List<String> strings = new LinkedList<>()
//在groovy 中,<>完全无用
call运算符
class MyCallable {
    int call(int x) {           
        2*x
    }
}

def mc = new MyCallable()
assert mc.call(2) == 4  //直接调用call方法        
assert mc(2) == 4   		//通过运算符调用对象的call方法,默认就是调用该方法

1.2.9 运算符优先级

LevelOperator(s)Name(s)
1new ()object creation, explicit parentheses
() {} []method call, closure, literal list/map
. .& .@member access, method closure, field/attribute access
?. * *. *:safe dereferencing, spread, spread-dot, spread-map
~ ! (type)bitwise negate/pattern, not, typecast
[] ?[] ++ --list/map/array (safe) index, post inc/decrement
2**power
3++ -- + -pre inc/decrement, unary plus, unary minus
4* / %multiply, div, remainder
5+ -addition, subtraction
6<< >> >>> .. ..<left/right (unsigned) shift, inclusive/exclusive range
7< <= > >= in !in instanceof !instanceof asless/greater than/or equal, in, not in, instanceof, not instanceof, type coercion
8== != <=> === !==equals, not equals, compare to, identical to, not identical to
=~ ==~regex find, regex match
9&binary/bitwise and
10^binary/bitwise xor
11|binary/bitwise or
12&&logical and
13||logical or
14? :ternary conditional
?:elvis operator
15= **= *= /= %= += -= <<= >>= >>>= &= ^= |= ?=various assignments

1.2.10运算符重载

class Bucket {
    int size

    Bucket(int size) { this.size = size }

  	//此方法为运算符重载
    Bucket plus(Bucket other) {                     
        return new Bucket(this.size + other.size)
    }
}

def b1 = new Bucket(4)
def b2 = new Bucket(11)
assert (b1 + b2).size == 15   //调用对象的plus方法                      

如下表为运算符重载表

OperatorMethodOperatorMethod
+a.plus(b)a[b]a.getAt(b)
-a.minus(b)a[b] = ca.putAt(b, c)
*a.multiply(b)a in bb.isCase(a)
/a.div(b)<<a.leftShift(b)
%a.mod(b)>>a.rightShift(b)
**a.power(b)>>>a.rightShiftUnsigned(b)
|a.or(b)++a.next()
&a.and(b)--a.previous()
^a.xor(b)+aa.positive()
asa.asType(b)-aa.negative()
a()a.call()~aa.bitwiseNegate()

1.3 程序结构

1.3.1 包名

//跟java 是一样的
package com.yoursite

1.3.2导包

// importing the class MarkupBuilder
import groovy.xml.MarkupBuilder

// using the imported class to create an object
def xml = new MarkupBuilder()

assert xml != null
默认导入包

以下的包均为默认导入的包

import java.lang.*
import java.util.*
import java.io.*
import java.net.*
import groovy.lang.*
import groovy.util.*
import java.math.BigInteger
import java.math.BigDecimal
单个包导入
// importing the class MarkupBuilder
import groovy.xml.MarkupBuilder

// using the imported class to create an object
def xml = new MarkupBuilder()

assert xml != null
批量导入
import groovy.xml.*

def markupBuilder = new MarkupBuilder()

assert markupBuilder != null

assert new StreamingMarkupBuilder() != null
静态导入
import static Boolean.FALSE

assert !FALSE //use directly, without Boolean prefix!
静态导入别名
import static Calendar.getInstance as now//使用as给导入的类或者方法起别名

assert now().class == Calendar.getInstance().class
静态批量导入
import static java.lang.Math.*

assert sin(0) == 0.0
assert cos(0) == 1.0
导入别名
import java.util.Date
import java.sql.Date as SQLDate

Date utilDate = new Date(1000L)
SQLDate sqlDate = new SQLDate(1000L)

assert utilDate instanceof java.util.Date
assert sqlDate instanceof java.sql.Date

1.3.3 脚本vs类

使用类方式实现Groovy world,需要定义一个类,并且让该类实现static void main(…)方法

class Main {                                    
    static void main(String... args) {          
        println 'Groovy world!'                 
    }
}

但是使用groovy 脚本方式,则无需如此

println 'Groovy world!'
脚本类

一个groovy 脚本一般是编译成一个类(Script),并且代码被拷贝到该类所实现的run方法,代码如下

import org.codehaus.groovy.runtime.InvokerHelper
class Main extends Script {                     
    def run() {                                 
        println 'Groovy world!'                 
    }
    static void main(String[] args) {           
        InvokerHelper.runScript(Main, args)     
    }
}
方法
int fib(int n) {
    n < 2 ? 1 : fib(n-1) + fib(n-2)
}
assert fib(10)==89

//另外在字符串中也可以直接调用方法
println 'Hello'                                 

int power(int n) { 2**n }                       

println "2^6==${power(6)}"   

//上面的代码最终将会转化为如下的代码
import org.codehaus.groovy.runtime.InvokerHelper
class Main extends Script {
    int power(int n) { 2** n}   //power方法称为Main类下的以一个成员方法
    def run() {
        println 'Hello'                         
        println "2^6==${power(6)}"              
    }
    static void main(String[] args) {
        InvokerHelper.runScript(Main, args)
    }
}
变量
//使用java方式进行变量定义
int x = 1
int y = 2
assert x+y == 3
//脚本中的变量无需声明类型
x = 1
y = 2
assert x+y == 3

//需要注意这两种定义方式是有区别的。
//使用声明类型的方式的变量,在其他方法内部是不可见的,但是在下面的方式定义的变量是全局可见的。

1.4 面向对象

1.4.1 类型

基本类型

Groovy支持的基本数据类型与java无异

  • 整型: byte (8 bit), short (16 bit), int (32 bit) 与 long (64 bit)
  • 浮点类型: float (32 bit) 跟 double (64 bit)
  • boolean 类型 (要么 truefalse)
  • char 类型 (16 bit, 等同于数字类型, 可以视为 UTF-16 编码,因为一共占用16为4个字节)
包装类型

Groovy 同样适用java的包装类型

Primitive type包装类型
booleanBoolean
charCharacter
shortShort
intInteger
longLong
floatFloat
doubleDouble
class Foo {
  static int i
}

assert Foo.class.getDeclaredField('i').type == int.class
assert Foo.i.class != int.class && Foo.i.class == Integer.class//实际上Groovy 是把变量i作为Integer进行存储的,而非int,

Groovy 中定义的类与Java的类基本上是一样的。但是还是有稍微的差异的 。

  • 类与方法在没有明显的类型修饰符的时候,默认是public 类型的,而非像java 使用private,但是有个特殊的注解可以让包处于默认private状态
  • 属性没有修饰符那么就会自动变为properties,何为属性,就是不需要显性的声明getter与setter
  • 类名无需与文件名一模一样
普通类
class Person {                       

    String name                      
    Integer age

    def increaseAge(Integer years) { 
        this.age += years
    }
}

def p = new Person()

内部类
class Outer {
    private String privateStr

    def callInnerMethod() {
        new Inner().methodA()       
    }

    class Inner {                   
        def methodA() {
            println "${privateStr}." 
        }
    }
}
抽象类
abstract class Abstract {         
    String name

    abstract def abstractMethod() 

    def concreteMethod() {
        println 'concrete'
    }
}
接口
interface Greeter {                                         
    void greet(String name)                                 
}
构造函数

Groovy提供3种方式调用构造函数

class PersonConstructor {
    String name
    Integer age

    PersonConstructor(name, age) {          
        this.name = name
        this.age = age
    }
}

//方式1 ,按照参数定义位置
def person1 = new PersonConstructor('Marie', 1)  
def person2 = ['Marie', 2] as PersonConstructor  //使用as强转
PersonConstructor person3 = ['Marie', 3]  //使用对象承接

命名参数

如果类没有声明默认的构造函数,可以通过以map方式作为参数,送入构造函数进行初始化

class PersonWOConstructor {                                  
    String name
    Integer age
}

def person4 = new PersonWOConstructor()                      
def person5 = new PersonWOConstructor(name: 'Marie')         
def person6 = new PersonWOConstructor(age: 1)                
def person7 = new PersonWOConstructor(name: 'Marie', age: 2) 

混合的情况

def foo(Map args, Integer number) { "${args.name}: ${args.age}, and the number is ${number}" }
foo(name: 'Marie', age: 1, 23)  
foo(23, name: 'Marie', age: 1)  //错误方法
默认参数
def foo(String par1, Integer par2 = 1) { [name: par1, age: par2] }//par2在没有传递的时候默认为1
assert foo('Marie').age == 1
可变参数
def foo(Object... args) { args.length }
assert foo() == 0
assert foo(1) == 1
assert foo(1, 2) == 2
异常声明
def badRead() {
    new File('doesNotExist.txt').text
}

//在方法的参数中声明异常
shouldFail(FileNotFoundException) {
    badRead()
}
关于字段与属性

字段(Field)是类的成员

  • 要求有访问修饰符 public protected private
  • 一个或多个可选修饰符static, final, synchronized
  • 可选的数据类型
  • 必要的名称
class Data {
    private int id                                  
    protected String description                    
    public static final boolean DEBUG = false       
}

属性(properties)

属性是一个对外可见的特征

  • 不可有访问修饰符
  • 其他与Field相同
class Person {
    String name                             
    int age                                 
}
//Groovy会为属性自动加上setter跟getter
//但是如果属性一旦被final修饰了,则不会自动创建setter跟getter
class Person {
    final String name                   
    final int age                       
    Person(String name, int age) {
        this.name = name                
        this.age = age                  
    }
}
class Person {
    String name
    void name(String name) {
        this.name = "Wonder$name"       
    }
    String wonder() {
        this.name                       
    }
}

def p = new Person()
p.name = 'Marge'            //不会调用setter            
assert p.name == 'Marge'          
p.name('Marge')             //自动调用setter            
assert p.wonder() == 'WonderMarge'      

//如果我想得到一个对象所有的属性(properites)怎么办?

class Person {
    String name
    int age
}
def p = new Person()
assert p.properties.keySet().containsAll(['name','age'])//直接调用对象的内置属性properites就可以得到全部定义的属性

class PseudoProperties {
    // 一个伪属性 "name"
    void setName(String name) {}
    String getName() {}

    // 一个伪只读属性 "age"
    int getAge() { 42 }

    //一个伪的只写属性 "groovy"
    void setGroovy(boolean groovy) {  }
}
def p = new PseudoProperties()
p.name = 'Foo'                      
assert p.age == 42                  
p.groovy = true                     



注解
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值