《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')

无论是双引号还是单引号,在三双引号字符串中需要被转义。

相关文章
|
XML 自然语言处理 Java
Kotlin 使用DSL构建语法结构 看这一篇就够了~
Kotlin 使用DSL构建语法结构 看这一篇就够了~
1549 0
Kotlin 使用DSL构建语法结构 看这一篇就够了~
|
Java
【Groovy】Groovy 动态语言特性 ( Groovy 语言与 Java 语言执行效率对比 | 以动态特性编译的 Groovy 类 | 以静态特性编译的 Groovy 类 )
【Groovy】Groovy 动态语言特性 ( Groovy 语言与 Java 语言执行效率对比 | 以动态特性编译的 Groovy 类 | 以静态特性编译的 Groovy 类 )
233 0
|
XML JSON Java
Groovy之高级语法
Groovy之高级语法
|
Java API 数据安全/隐私保护
【Groovy】Groovy 语言特点简介 ( 支持 Java 语法 | 支持 Java 虚拟机 | Groovy 语言是动态语言 | Groovy 扩展 JDK | 编译时元编程 )
【Groovy】Groovy 语言特点简介 ( 支持 Java 语法 | 支持 Java 虚拟机 | Groovy 语言是动态语言 | Groovy 扩展 JDK | 编译时元编程 )
302 0
|
Java
【Groovy】Groovy 代码创建 ( 使用 Java 语法实现 Groovy 类和主函数并运行 | 按照 Groovy 语法改造上述 Java 语法规则代码 )
【Groovy】Groovy 代码创建 ( 使用 Java 语法实现 Groovy 类和主函数并运行 | 按照 Groovy 语法改造上述 Java 语法规则代码 )
214 0
【Groovy】Groovy 代码创建 ( 使用 Java 语法实现 Groovy 类和主函数并运行 | 按照 Groovy 语法改造上述 Java 语法规则代码 )
|
JSON Java C#
Groovy核心语法总结
Groovy是一种基于JVM,功能强大、类型可选,动态的、可与Java无缝衔接的编程语言。Groovy语法简洁、易于学习,可以大大提高开发人员的工作效率,可为Java程序添加强大的功能,包括脚本功能、特定领域的语言编写、运行时和编译时元编程以及函数式编程。本文将快速介绍核心语法,让读者快速入门。
1193 0
 Groovy核心语法总结
|
JavaScript
ECMAScript 2018 语言规范正式发布,改进正则表达式
ECMAScript 2018(第九版 JS)已于 6 月底正式发布,带来了许多新特性。ECMAScript 2018 于今年2月出炉草案,TC39 技术委员会每两个月开会一次,讨论当前草案的现状。
1497 0
|
Kotlin SQL
Kotlin领域特定语言(DSL)
一、DSL的概念 只在特定领域内使用的语言 例如: —HTML、Gradle、SQL等等 特点: 计算机编程语言 具有语言的表达能力 有限的表达能力 关注某个特定的领域 二、下面用DSL来写一个例子吧 需要下面五个类: 三、创建一个Node节点的接口 package cn.
1322 0