注释
- 单行注释//
//这是单行注释
- 多行注释/**/
/*这是多行注释*/
// GroovyDoc注释/** */
/**
* 这是文档注释
*/
变量名
- 普通变量名和Java规则一样
- 引用变量,变量点后面可以跟引用值,可以使用java不允许的符号比如破折号等,比如
map = [:]
map."name" = "james"
map."a-b-c" = "dota"
map./slashy string/
map.$/dollar slashy string/$
def firstname = "Homer"
map."Simpson-${firstname}" = "Homer Simpson"
字符串
- 单引号字符串
'hello world'
- 三引号字符串表示多行
""" line 1
line 2
line 3
- 双引号字符串,可以使用或者{}在字符串中进行插值,其中
${}
可以插入值或者表达式,但是表达式必须要以某种形式返回值否则值为null。
其中$
只对a.b有效,对其他语句无效如方法调用,大括号等,如果想输入$符号可以使用""
"2 + 3 is ${2+3}" // output 2+3 is 5
"2 + 3 is ${def i = 2; def j = 3; i+j}" // output 2 + 3 is 5
def person = [name: 'James', age: 36]
"$person.name is 36" // output James is 36
"person is $def age = 10" // 不合法
""
- 当字符串中存在${->}形式,这表示一个闭包,闭包包含0个参数或者1个参数
0个参数的闭包会调用toString()作为值,即->后面的值会toString,如果是表达式必须要有返回值,否则为null
1个参数的闭包会传入的参数类型为StringWriter,用这个参数可以追加内容, 可以使用"<<"操作符追加内容
def person = "1 + 2 is ${->3}"
def person = "1 + 2 is ${w -> w.append("5")}"
def person = "1 + 2 is ${w -> w << "5"}"
- 上述两种插值方法对比,采用闭包的好处可以动态更新数据,如果变量值更新了,闭包所用参数也对应更新。
如下所示可以看出,当num值更新时,字符串闭包内的值也改变了, 普通传值没有改变是因为创建String的时候值就已经确定了。
def num = 3
def normal = "1 + 2 is ${-> num}"
def cls = "1 + 2 is ${num}"
println(normal) // output "1 + 2 is 3"
println(cls) // output "1 + 2 is 3"
num = 5
println(normal) // output "1 + 2 is 3"
println(cls) // output "1 + 2 is 5"
- 字符串使用插值,类型则会被转换为GString,类型,值得注意的是GString类型的hashCode和String类型的hashCode是不一样的
def key = "a"
map = [${key}:"James"]
printfln map["a"] // output null
- Char类型可以用以下三种来表示
def a = 'A'
def b = 'A' as char
def c = (char)'A'
Numbers
- 整数数据类型基本和Java一样分别是
byte
、short
、char
、int
、long
、BigInteger
如果类型修饰符为def
,会根据范围,自动识别对应的数据类型def i = 10 println i instance of Interger // Output ture
- 小数数据类型分别是
float
、double
、BigDecimal
float a = 1.2 double b = 1.21
- 数字可以添加下划线,使某些大数看起来更加清楚
long i = 1223_123_1L
long i = 0x7fff_8fff_0000_ffff
- 通过数字后缀来代表数据类型
Integer a = 42I或42i
Long b = 42L或42l
BigInteger c = 42G或42g
Double d = 42.2D或42.5d
Float e = 42.2F或42.1f
BigDecimal f = 42.24G或42.24g
List
- Groovy使用逗号分隔值(用方括号括起来)来表示列表,默认类型为List, 一个List的值可以是不同类型
def listone = [1,2,3,4]
def listtwo = [1, 1.24, "hello"]s
- List是ArrayList的实例,可以通过as进行转换成其他类型的List
def list = [1,2,3,4] as LinkedList
- List索引可以使用以下几种形式, 可以用<<添加值
def list = [1,2,3,4]
list[-1]
list[2]
list[1..3]
list << 5
- 多维List
list = [[0,1],[2,3]]
list[0][1]
Arrays
- Arrays的形式上和List是一样的,但是如果需要Arrays类型数据,需要通过变量指定数据类型或者通过as强转
int[] list = [1,2,3,4,5]
def list = [1,2,3,4] as int[]
- 索引,多维数组等定义和List一样
Maps
- Groovy使用逗号分隔值(用方括号括起来),键值对冒号分隔来表示Map
def colors = [red: '#FF0000', green: '#00FF00', blue: '#0000FF']
colors['pink'] = '#FF00FF'
colors.yellow = '#FFFF00'
- 如果想要引用变量作为key值,则需要加上括号
def key = 'pink'
def map = [(key) :123]
操作符
- ** 表示次方, 下面表示2的3次方
def num = 2 ** 3
- 双目运算符?:,是一个false 赋值
def a = 8
def b = a < 7 ?:10
// 等价于
if(a < 7) {
b = ture
} else {
b = 10
}
- *.操作符,将cars中make元素全部收集到同一个列表
class Car {
String make
String model
}
def cars = [
new Car(make: 'Peugeot', model: '508'),
new Car(make: 'Renault', model: 'Clio')]
def makes = cars*.make
assert makes == ['Peugeot', 'Renault']
- Spread操作符可以将list列表中值传入符合list数量的方法中, 也可以直接插入到另外一个list中
def list = [1,2,3]
int f(int i, int j, int z) {
return i * j + z
}
f(*list)
def other = [4,5, *list] // output [4,5,1,2,3]
def other = [4,5, list] // output [4,5,[1,2,3]]
- map中也可以使用
def map = [red : 0x1]
def add = [green : 0x2, * : map]
// output [green:34, red:17]
- Range操作符.., 生成一个有序列表
def i = 0..5 // output [0,1,2,3,4,5]
def i = 0..<5 // output [0,1,2,3,4]
def i = 'a'..'d' // output ['a','b','c','d']
- 比较操作符<=>, 类似java中的compareTO方法,
(1<=>1) = 0
(2<=>1) = 1
(2<=>3) = -1
- 下标操作符,类似getAt()或putAt()方法
def list = [0,1,2,3,4]
list[3] = 5 // output [0,1,2,5,4]
list[4] // output 4
- 成员关系操作符in,判断一个数据成员是否在列表当中
def list = [1,2,3]
5 in list // output false
- Identity操作符,==操作符在Groovy和Java中使用是不一样的, ==比较是值是否相等,equals比较的是内存地址是否一样
def list1 = [1,2,3]
def list2 = [1,2,3]
list1.is(list2) // output false
list1 == list2 // output true
Integer a = new Integer(5);
Integer b = new Integer(5);
println a.is(b) // output false
println a == b // output true
- 转换操作符as,as操作符如果符合转换规则,就可以转换,否则会抛异常。如果转换为另外一个类型就会创建一个新的对象
Integer x = 123 as String // output "123"
String a = "123" as Map // 抛异常
- Groovy支持运算符重载
包
- 包支持用别名
import java.util.Date as SQLDate
SQLDate date = new SQLDate()
- 脚本和类,Groovy会把脚本转换成一个类,这个类继承于
Script
,最终调用的还是main()
方法,我们在写脚本中写的方法会被加入到类中,写的语句全部在run()方法中。
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}
def run() {
println 'Hello'
println "2^6==${power(6)}"
}
static void main(String[] args) {
InvokerHelper.runScript(Main, args)
}
}
- 脚本中变量作用域。
变量如果声明类型,则无法被其他方法访问,因为这个变量相当于run方法中的局部变量
变量如果没有声明类型,则会被脚本绑定,脚本会和其他方法共享这些数据
int declared = 0
undeclared = 10
void pow() {
println declared
println undeclared
}
pow()
- 反编译之后的代码, 可以看到run方法中会把变量值传入,pow方法中把值取出来
public class Study extends Script
{
public void pow()
{
CallSite[] arrayOfCallSite = $getCallSiteArray();
arrayOfCallSite[2].callCurrent(this, arrayOfCallSite[3].callGroovyObjectGetProperty(this));
arrayOfCallSite[4].callCurrent(this, arrayOfCallSite[5].callGroovyObjectGetProperty(this));
}
public Object run()
{
CallSite[] arrayOfCallSite = $getCallSiteArray();
int declared = 0;
int i = 10;
ScriptBytecodeAdapter.setGroovyObjectProperty(Integer.valueOf(i), Study.class, this, (String)"undeclared");
pow();
}
public static void main(String... args)
{
CallSite[] arrayOfCallSite = $getCallSiteArray();
arrayOfCallSite[0].call(InvokerHelper.class, Study.class, args);
}
}
方法和类
- 如果没有可见修饰符修饰,默认是public的,并且会自动转换成属性,自然省去get和set方法
public class Study {
public String name;
class School {
Study study = new Study();
public void driver() {
}
}
}
一个文件可以包含多个类,如果没有类默认是脚本(当然最后还是会转换为一个类)
不论是构造函数还是普通函数参数都可以通过列表传入, 方法参数可以设置默认值
public class Study {
public Study(String name, int age) {
}
public void pow(int a, int b = 9) {
}
}
Study study = ["1",2]
study.pow([5,6])
study.pow(5)
- 方法可变参数列表
可以传入任意个参数
T[]和T..是等价的,也表示可见参数,意味方法参数列表为数组的,都表示可变参数。
如果出现多个方法重载,比如一个方法参数列表是可见参数,另外一个方法参数列表是具体个数参数,以含具体个数参数方法为主void f(int[] a); void f(int a); f(1) // 调用的是f(int a)方法
Traits(特征)
- 是Groovy独有的面向对象特性,有几点特性:由行为构成、可以实现接口、行为可以重载,兼容静态类型的检查和编译。可以看成具有方法实现和状态的接口,关键字
trait
trait FlyingAbility {
String fly() {
println "I'm flying!"
}
}
class Bird implements FlyingAbility {}
def b = new Bird()
println b.fly()
- 可以含有抽象方法
trait Greetable {
abstract String name()
String greeting() { "Hello, ${name()}!" }
}
- 可以定义
private
方法
trait Greeter {
private String greetingMessage() {
'Hello from a private method!'
}
String greet() {
def m = greetingMessage()
println m
}
}
- 可以使用
this
操作符
public trait FlyingAbility {
String fly() {
return this
}
}
- 可以定义属性
trait Named {
String name
}
- 可以定义私有和共有字段
trait Counter {
public int num = 1
private int count = 0
int count() { count += 1; count }
}
- 和接口一样支持多重继承
trait FlyingAbility {
String fly() { "I'm flying!" }
}
trait SpeakingAbility {
String speak() { "I'm speaking!" }
}
class Duck implements FlyingAbility, SpeakingAbility {}
- 方法可以被覆盖、相互继承
trait FlyingAbility {
String fly() {
"I'm flying!"
}
}
trait SpeakingAbility extends FlyingAbility {
String fly() {
"I can speaking!"
}
}
闭包
- 闭包是一个匿名函数,拥有函数的特征,写法如下
{ [closureParameters -> ] statements }
, 有以下几种写法
{ item++ }
{ -> item++ }
{ println it }
{ it -> println it }
{ name -> println name }
{ String x, int y ->
println "hey ${x} the value is ${y}"
}
{ reader ->
def line = reader.readLine()
line.trim()
}
- 闭包作为一个对象,是
groovy.lang.Closur
一个实例,可以赋值给该类的实例对象
def listener = { e -> e }
println listener //output `groovy.lang.Closure`对象
println listener("lis") // output "lis"
Closure callback = { println 'Done!' }
Closure<Boolean> isTextFile = {
File it -> it.name.endsWith('.txt')
}
- 闭包可以像其他方法一样被调用,可以像其他函数一样传参进入,也可以当成一个对象调用
call
方法
def isOdd = {
int i -> i%2 != 0
}
isOdd(2)
isOdd.call(2)
- 闭包跟函数一样,参数列表可以是对象,基本类型,可选列表,也可以给参数设置默认值,
不过和函数不同的地方,如果闭包没有确切定义一个参数列表,闭包就会定义一个隐式参数it
def f = {-> printfln it}
def f = {it -> println it}
闭包中的this
,owner
,delegate
- this:在一个类中定义闭包,闭包中的this指向该类,
getThisObject()
和this
是等价的。
类中定义闭包使用this,则指向内部类,在该闭包中可以调用该类的成员变量和方法
闭包中嵌套使用闭包,this也是指向类
总之,无论闭包怎么嵌套,闭包中使用this就指向离他最近的类
class Enclosing {
void run() {
def whatIsThisObject = { getThisObject() }
assert whatIsThisObject() == this
def whatIsThis = { this }
assert whatIsThis() == this // 等价
}
}
class EnclosedInInnerClass {
class Inner {
Closure cl = { this }
}
void run() {
def inner = new Inner()
// 内部类定义闭包使用this,则指向内部类
assert inner.cl() == inner
}
}
class NestedClosures {
void run() {
def nestedClosures = {
def cl = { this }
cl()
}
// 闭包嵌套闭包,则this也指向类
assert nestedClosures() == this
}
}
owner: 指向一个封闭的对象,可以是一个类或者闭包
owner
和getOwner()
是等价的
如果闭包定义在内部类中,指向的也是这个内部类
但是如果owner
在嵌套闭包内,则指向的是outer闭包,并非和this一样是类
总结:和this一样都是返回封闭对象,但是this值包含类,owner包含了闭包和类-
delegate: 委托可以被赋值成定义的任何对象,默认情况下,
delegate
和owner
的指向范围一样
但是区别是delegate
可以被赋于其他对象,而owner
则不行class Person { String name } class Thing { String name } def p = new Persong(name: "Normal") def t = new Thing(name : "Teapot") def a = { delegate.name.toUpperCase() } def b = { owner.name.toUpperCase() } a.delegate = p a() // output NORMAL b.owner = t b() //报错,因为不允许被转为t对象