文章目录
索引
当前主要看了Language Specification章节,但是Semantics小节里面的类型检查和静态编译基本算跳过
文档地址: http://groovy-lang.org/documentation.html
基本类型之间的转化: http://groovy-lang.org/differences.html#_conversions
基本类型数学计算之间的转化:http://groovy-lang.org/syntax.html#_math_operations
关键词:http://groovy-lang.org/syntax.html#_keywords
转义字符:http://groovy-lang.org/syntax.html#_escaping_special_characters
所有操作符优先级 http://groovy-lang.org/operators.html#_operator_precedence
(这里的 >>> 和 >>= >>>= 在文档里没有提到啊)
操作符重载:http://groovy-lang.org/operators.html#Operator-Overloading
http://groovy-lang.org/semantics.html 这个文档下面的type check和static compile瞄了两眼跳了
类型检查规则:http://groovy-lang.org/semantics.html#static-type-checking
静态编译:http://groovy-lang.org/semantics.html#_static_compilation
使用Tuple注解快捷创建构造器 : http://docs.groovy-lang.org/2.5.4/html/gapi/index.html?groovy/transform/TupleConstructor.html
使用Map注解快捷创建构造器 : http://docs.groovy-lang.org/2.5.4/html/gapi/index.html?groovy/transform/MapConstructor.html
多注解出现的冲突解决: http://groovy-lang.org/objectorientation.html#handling_duplicate_annotations
http://docs.groovy-lang.org/latest/html/documentation/core-domain-specific-languages.html
http://docs.groovy-lang.org/latest/html/documentation/core-semantics.html#closure-coercion
方法指针:
http://docs.groovy-lang.org/latest/html/documentation/core-operators.html#method-pointer-operator
语法
鉴于groovy和java的相似性,大概贴出需要额外记录的部分
//shebang,必须在第一行
#!/usr/bin/env groovy
基本
定义
def 或者 显示类型声明,或者强转(as 或者 类型),如:
def 在Groovy里就是 Object
char c1 = 'A'
def c2 = 'B' as char
def c3 = (char)'C'
`cx`.asType(char)
groovy中的true
非false,引用非null,size非0,大小非0
可以通过重写asBoolean()来重新计算
常用
//操作
assert 2 ** 3 == 8
//spaceship
assert (1 <=> 1) == 0
assert (3 <=> 1) == 1
assert ('a' <=> 'z') == -1
//属性调用
//field调用,感觉第一种方式拒绝了mName的命名方式啊
person.name //默认调用person.getName()
person.@name //调用person的可被访问field : name
//elvis 操作符
displayName = user.name ? user.name : 'Anonymous'
displayName = user.name ? : 'Anonymous'
//判空
person?.name
//数组
//需要显示定义
String[] arrStr = ['Ananas', 'Banana', 'Kiwi']
//列表
//等同于list.append('e')
list << 'e'
//索引
letters[-1]
letters[1, 3]
letters[2..4]
//字典
//可以用(key)来将key的值作为键而不是`key`
person = [(key): 'Guillaume']
//基本数字类型
byte, char, short, int, long, BigInteger, Double, BigDecimal
二进制:0b 开头; 八进制:0;16进制:0x
后缀:i,f,l,d,g(big) ; 科学计数法:5e-1
//属性定义
//可以直接用字符串拼接,支持所有类型
map."any name-is ok" = "ALLOWED"
快捷操作
//iterable
//遍历
// *. 获取cars的所有实例,并一一获取make后添加到一个list 等同于 cars.collect{ it.make }
// 所有实现Iterable 接口的类都可以使用 *.
// 嵌套使用效果更佳,但是collection建议使用 cars.collectNested{ it.model }
def cars = [
new Car(make: 'Peugeot', model: '508'),
new Car(make: 'Renault', model: 'Clio')]
def makes = cars*.make
assert makes == ['Peugeot', 'Renault']
//引用
args = [4]
assert function(*args,5,6) == 15
def m1 = [c:3, d:4]
assert [a:1, b:2, *:m1] == [a:1, b:2, c:3, d:4]
//range
//可以对任何实现了Comaprable(具有next和previous方法)的对象使用
assert (0..5).collect() == [0, 1, 2, 3, 4, 5]
assert (0..<5).collect() == [0, 1, 2, 3, 4]
assert (0..5) instanceof List
import
import 支持 * (叫做star import)
支持static,并且允许导入不同参数的同名方法(同参也可以,但是优先使用import的)
//单例简写
import static Calendar.getInstance as now
assert now().class == Calendar.getInstance().class
//别名
import java.util.Date
import java.sql.Date as SQLDate
这些包都是默认导入的:
java.io.*;
java.lang.*;
java.math.BigDecimal;
java.math.BigInteger;
java.net.*;
java.util.*;
groovy.lang.*;
groovy.util.*
正则
//以 ~ 方式快速创建,支持任何形式的字符串
def p = ~/foo/
assert p instanceof Pattern
//用 =~ 构建Matcher
def text = "some text to match"
def m = text =~ /match/
assert m instanceof Matcher
if (!m) { //对Matcher做比较时,等同于调用m.find()
throw new RuntimeException("Oops, text not found!")
}
//快速返回boolean结果
m = text ==~ /match/
as
as 对应在 asType() 方法的实现,如果不是同一个类型,会创建新对象
//类内部实现方法
def asType(Class target) {
if (Cartesian==target) {
return new Cartesian(x: r*cos(phi), y: r*sin(phi))
}
}
//类外部实现
Polar.metaClass.asType = { Class target ->
if (Cartesian==target) {
return new Cartesian(x: r*cos(phi), y: r*sin(phi))
}
}
使用反射时注意
greeter = { println 'Hello, Groovy!' }.asType(clazz)
闭包
{ w -> w << 3}
{ -> number }
//方法指针(pointer)
def fun = str.&toUpperCase
assert fun() == str.toUpperCase()
这里的pointer是闭包类型
as可以把闭包改成任意类型
甚至类里面已定义了的方法都可以被覆盖
注意:这里的替换是把闭包里的内容,全部放入方法体,如果有两个方法,两个方法都会被覆盖。
那怎么对多个覆盖呢。有map:
def map
map = [
i: 10,
hasNext: { map.i > 0 },
next: { map.i-- },
]
def iter = map as Iterator
closure是 一个开放的,匿名的,块状的代码,是groovy.lang.Closure的实例,可以接受参数,返回值并被赋值给一个变量
def variable = { [closureParameters -> ] statements }
variable()
variable.call()
隐式it参数
def greeting = { "Hello, $it!" }
assert greeting('Patrick') == 'Hello, Patrick!'
//为了杜绝无用调用,因为你可能没有使用it参数
def magicNumber = { -> 42 }
this,owner,delegate
闭包中使用 getThisObject() 和 this 效果一样,都是获取最近的outer class实例
getOwner()和owner的效果一样,owner则是获取闭包的最直接持有者
getDelegate()和delegate一样,日常作用和owner一样,但是可以被赋值
你可以通过设置delegate实现代理模式:
class Person {
String name
}
def p = new Person(name:'Igor')
//正常模式,即你也可以定义一个变量代替delegate实现相同效果
def upperCasedName = { delegate.name.toUpperCase() }
upperCasedName.delegate = p
assert upperCasedName() == 'IGOR'
甚至delegate都可以省略,省略的时候可以选择策略
p.pretty.resolveStrategy = Closure.DELEGATE_FIRST
(因为默认情况下delegate和owner一样;
如果delegate省略,系统自动补全,而补全的时候,可以先/只补全owner的属性,或者delegate,或者TO_SELF
TO_SELF只是在自定义Closure的时候有意义)
//简写模式
def cl = { name.toUpperCase() }
cl.delegate = p
assert cl() == 'IGOR'
GString中
def x = 1
def gs = "x = ${x}"
assert gs == 'x = 1'
x = 2
assert gs == 'x = 1'
当重新赋值x = 2时,实际上是产生了一个新的x2,而不是原先的那个x1了.对于
x
而
言
,
实
际
上
是
{x}而言,实际上是
x而言,实际上是x的表达式,即x1的引用
如果要不变,可以用这种方法:"x = ${-> x}"
,或者让x成为一个引用变量是一样的效果,这样就会获取现在的x2
curry
在groovy中,闭包允许设置一个最左或者最右参数
//你可以选择传入n,或者str,或者两者
def nCopies = { int n, String str -> str*n }
def twice = nCopies.curry(2)
assert twice('bla') == 'blabla'
def blah = nCopies.rcurry('bla')
assert blah(2) == 'blabla'
assert twice('bla') == nCopies(2, 'bla')
也允许设置索引参数
def volume = { double l, double w, double h -> l*w*h }
def fixedWidthVolume = volume.ncurry(1, 2d)
assert volume(3d, 2d, 4d) == fixedWidthVolume(3d, 4d)
def fixedWidthAndHeight = volume.ncurry(1, 2d, 4d)
assert volume(3d, 2d, 4d) == fixedWidthAndHeight(3d)
Memoization
fib = { long n -> n<2?n:fib(n-1)+fib(n-2) }.memoize()
Composition
def plus2 = { it + 2 }
def times3 = { it * 3 }
def times3plus2 = plus2 << times3
assert times3plus2(3) == 11
assert times3plus2(4) == plus2(times3(4))
def plus2times3 = times3 << plus2
assert plus2times3(3) == 15
assert plus2times3(5) == times3(plus2(5))
// reverse composition
assert times3plus2(3) == (times3 >> plus2)(3)
Trampoline
常规的递归调用,可能会造成堆栈溢出,而Trampoline可以避免这种情况
理解成平行方法调用,而不是垂直栈
$
$ 符号可以用来标记引用,后文直接称为$引用
直接变量和闭包 使用${}
包裹;带.
的变量 直接用$
作为前缀
def firstname = "Homer"
"${firstname}"
//闭包
${ w -> w << 3}
${ -> number }
def person = [name: 'Guillaume', age: 36]
"$person.name"
字符串
根据包裹的符号和实际内容,被解释成多种类型:
- 单引号:常规String
- 双引号:如果字符串包含
$
,会被解释成GString。GString 支持$引用
和闭包 - 三个单引号: 三个单引号的字符串直接可以换行、缩进,不需要对应的符号显式表示
def strippedFirstNewline = '''\ //这里的'\'可以去除默认的第一行换行
line one
'''
- 三个双引号:综合了 双引号 和 三个单引号 的特性
- Slashy: 用
/
包裹,类似 三个双引号,好处是不用转义\
。适合用于正则
因为用于正则,所以可以特别使用 $()。
- Dollar slashy:功能类似slashy, 但是用
$
转义。转义有点没看懂(好像是不用转,但是连续两个会被处理)
def dollarSlashy = $/
$ dollar sign
$$ escaped dollar sign
\ backslash
/ forward slash
$/ escaped forward slash
$$$/ escaped opening dollar slashy
$/$$ escaped closing dollar slashy
/$
assert [
'$ dollar sign',
'$ escaped dollar sign',
'\\ backslash',
'/ forward slash',
'/ escaped forward slash',
'$/ escaped opening dollar slashy',
'/$ escaped closing dollar slashy'
].every { dollarSlashy.contains(it) }
控制语句
switch
case支持闭包和列表:
case 12..30:
result = "range"
break
case ~/fo*/: // toString() representation of x matches the pattern?
result = "foo regex"
break
case { it < 0 }: // or { x < 0 }
result = "negative"
break
for循环
支持java的fori,for-each,也支持in:
for ( i in 0..9 ) {
x += i
}
assert:
assert [left expression] == [right expression] : (optional message)
assert calc(x,y) == z*z : 'Incorrect computation result'
enum
在常规情况和switch情况,Enum和String之间会自动转化
但是作为方法参数的时候,还是需要as 转化下
enum State {
up,
down
}
State st = 'up'
assert st == State.up
def val = "up"
State st = "${val}"
assert st == State.up
SAM(single abstract method)类型:
是一种只定义了一个抽象方法的类型,包括功能型接口,和抽象类
而所有闭包都可以用as操作符转化成sam类型,理解起来有点像直接把闭包赋值给方法作为方法体
interface Predicate<T> {
boolean accept(T obj)
}
abstract class Greeter {
abstract String getName()
void greet() {
println "Hello, $name"
}
}
//2.2.0 之后可以直接省略 as
Predicate filter = { it.contains 'G' } as Predicate
assert filter.accept('Groovy') == true
Greeter greeter = { 'Groovy' } as Greeter
greeter.greet()
类
类和方法,无可访问性修饰符的时候,默认为Public
field,无可访问性修饰符的时候,默认具有getter,setter
文件名和类名可以不同,但是推荐相同,因为脚本执行用文件名作为类名
获取类的所有属性: obj.properties.keySet()
方法
构造器和方法在定义和使用上别无二致,
//要随意顺序传入map的参数,需要args是方法的第一个参数。如果不是第一个参数,必须对应传入Map类型参数
//默认参数必须放在参数序列的最后面
def foo(Map args, Integer number = 2) { "${args.name}: ${args.age}, and the number is ${number}" }
foo(name: 'Marie', age: 1, 23)
foo(23, name: 'Marie', age: 1)
都支持positinal parameter和named parameter
//构造器
class PersonConstructor {
String name
Integer age
PersonConstructor(name, age) {
this.name = name
this.age = age
}
}
//position式使用(参数叫positional parameter)
def person1 = new PersonConstructor('Marie', 1)
def person2 = ['Marie', 2] as PersonConstructor
PersonConstructor person3 = ['Marie', 3]
//如果没有指定构造器,可以map式调用(参数叫named parameter)
//使用map,需要第一个就是map式,后面随意,可以和position式混用
def person7 = new PersonWOConstructor(name: 'Marie', age: 2)
类可以向外赋值
def coordinates = new Coordinates(latitude: 43.23, longitude: 3.67)
def (la, lo) = coordinates
assert la == 43.23
assert lo == 3.67
其他
- GString 的hashcode和常规的String的不同。禁止把GString 作为map的键
- in 对应 isCase() 方法,在List中对应 contains() 方法
- 只要类实现call方法,调用类默认方法,相当于调用call
def mc = new MyCallable(); mc(2) == mc().call(2)
- lable, 用来增加可读性和break跳转,后者不是一个好的编码风格;可以被AST转化
- GPath:解析POJO或xml的时候,都是使用.作为分割点,如
a.@href
- 如果方法有参数,且无混淆项,调用时括号可以被省略
- 末尾分号可被省略
- 方法、闭包结尾语句,可省略return。最后一句的结果被返回
- public可省略
- 方法返回参数也是可选的,但是如果不写,需要写上修饰符,以区分方法调用和声明
大括号{ }
用来作为闭包语法,不能用作创建数组(中括号[]
可以)- 忽略field的(使用范围)修饰词会认为是一个private field,同时有getter,setter;
package-private对应需要用@PackageScope修饰 - 万物皆object。所以groovy中的基本变量其实都会被转化成包装类(wrapper class)
- 如果你定义了一个getter或setter,那么对应的属性,即使没有显式声明也可以读取或者设置
class Foo {
static int i
}
assert Foo.class.getDeclaredField('i').type == int.class
assert Foo.i.class != int.class && Foo.i.class == Integer.class
闭包注解
//定义时,把value定义成class类型
@Retention(RetentionPolicy.RUNTIME)
@interface OnlyIf {
Class value()
}
//注解使用
@OnlyIf({ jdk>=7 && windows })
void requiresJDK7AndWindows() {
result << 'JDK 7 Windows'
}
//注解生效代码示例,上述requiresJDK...方法定义在Task类中
class Runner {
static <T> T run(Class<T> taskClass) {
def tasks = taskClass.newInstance()
def params = [jdk:6, windows: false]
tasks.class.declaredMethods.each { m ->
if (Modifier.isPublic(m.modifiers) && m.parameterTypes.length == 0) {
def onlyIf = m.getAnnotation(OnlyIf)
if (onlyIf) {
Closure cl = onlyIf.value().newInstance(tasks,tasks)
cl.delegate = params
if (cl()) {
m.invoke(tasks)
}
} else {
m.invoke(tasks)
}
}
}
tasks
}
}
def tasks = Runner.run(Tasks)
assert tasks.result == [1, 'JDK 6'] as Set
meta注解
可以理解成注解别名,一般代表了一个或多个其他注解,主要为了减少代码量。
//service和transactional是其他注解
import groovy.transform.AnnotationCollector
@Service
@Transactional
@AnnotationCollector
@interface TransactionalService {
}
因为多注解就可能造成冲突,此时需要修改处理器模式或者自定义处理器
Traits
用来实现
composition of behaviors、runtime implementation of interfaces、behavior overriding、compatibility with static type checking/compilation
使用类似接口
[@SelfType(ChildClassType)]
trait Temp {
public String name
//只有private或public
private String greetingMessage() {
'Hello from a private method!'
}
abstract String name()
String fly() { "I'm flying!" }
}
//调用时,属性名前面增加__
obj.Temp__name
//包名变更,所有点变成_
my_package_Temp__name
虽然概念上A implement trait可以理解成A继承trait,但是实际上trait是直接被放入类A里的,所以trait里的方法返回this实际上是返回A实例
但是如果trait implement interface , 此时trait就有了实例
这段话没看懂
While it would likely be considered bad style to inherit and override or multiply inherit methods with the same signature but a mix of final and non-final variants, Groovy doesn’t prohibit this scenario. Normal method selection applies and the modifier used will be determined from the resulting method. You might consider creating a base class which implements the desired trait(s) if you want trait implementation methods that can’t be overridden.
trait 单继承trait 用extend,多继承用implement
动态代码,可以调用quack(),而quack不在trait中声明
trait SpeakingDuck {
private Map props = [:]
String speak() { quack() }
def propertyMissing(String prop) {
props[prop]
}
}
class Duck implements SpeakingDuck {
String methodMissing(String name, args) {
"${name.capitalize()}!"
}
}
def d = new Duck()
assert d.speak() == 'Quack!' //因为没有quack方法,所以根据methodMissing输出他的名字
多继承冲突
如果两个方法冲突,默认取后者,可以通过重写对方法:A.super.conflictMethod()
动态实现:new Something() as A、new St().withTraits A, B 这时候要注意,产生的实例已经不是最开始的那个
//三个都是trait
class Handler implements Parent, Child1, Child2 {}
def h = new Handler()
h.on('foo', [:])
//这里child1,child2都继承parent,on是Parent的方法
//调用h.on的时候,会先去看child2里对on的处理,如果调用了super.on,会去child1里看,如果调用了super.on,会去Parent里看
//如果child2和parent无关,那么super.on会调用handler.super(这个特性可以用来修饰final类的方法)
如果只有一个抽象方法(SAM),可以直接闭包创建:
trait Greeter {
String greet() { "Hello $name" }
abstract String getName()
}
Greeter greeter = { 'Alice' }
官方不支持AST(Abstract syntax tree)转化
trait的变量不被继承,但是可以用getX,getY变相获取子类的变量;同样的原因,不能使用x++,可以使用 x += 1
mixin
混搭。。trait时,会改变o的instanceOf,而这个方法不会
class A { String methodFromA() { 'A' } }
class B { String methodFromB() { 'B' } }
A.metaClass.mixin B
def o = new A()
assert o.methodFromA() == 'A'
assert o.methodFromB() == 'B'
assert o instanceof A
assert !(o instanceof B)
static method,property,field
http://groovy-lang.org/objectorientation.html#_static_methods_properties_and_fields
静态成员的支持还是实验性的,只支持2.5.4, 略过
执行
命令
groovy -version
groovysh
groovyConsole
groovy <scriptName>
脚本和类的理解
//常规类写法
class Main {
static void main(String... args) {
println 'Groovy world!'
}
}
//常规脚本写法。
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)
}
}
脚本(script)会被编译成类(class),这里的run需要有一个返回值,而且脚本的名字来源于文件名
在脚本中定义变量,int x = 1 会限制其作用于在run方法内
而 x = 1,会将x绑定到整个脚本,甚至可以和交互的应用共享数据。
如果你只是想把范围扩大到class,而不要绑定,可以使用@Field标签
和java 运行、语法区别
- 因为类型在运行的时候才知道,所以方法的选择会在运行时进行。这种方式叫做 运行时分发(runtime dispatch) 或 多方法(multi-methods)
//这里 result = 1
//因为在运行时会忽略定义时的Object;然后“object”是String类型的
int method(String arg) {
return 1;
}
int method(Object arg) {
return 2;
}
Object o = "Object";
int result = method(o);
- 不支持java7的ARM(Automatic resource manage)语法,但是下面写法有类似效果:
new File('/path/to/file').eachLine('UTF-8') {
println it
}
new File('/path/to/file').withReader('UTF-8') { reader ->
reader.eachLine {
println it
}
}
- 匿名函数和java表现的类似,实际实现和groovy.lang.closure相近,总是有些不同:
//非静态内部类的调用
//groovy支持调用单参数函数时不传递参数,这时参数为null;构造器也会有这个属性,可能造成问题(但是还没有更好的方式解决)
public class Y {
public class X {}
public X foo() {
return new X()
}
public static X createX(Y y) {
return new X(y)
}
}
- lambdas
//groovy不支持这种语法,但是用闭包有相近的部分
Runnable run = { println 'run' }
list.each { println it } // or list.each(this.&println)
- java中的
==
和 groovy的is
一样,而groovy的==
被解释成a.compareTo(b) == 0
- 接口暂时不支持java8 的default,可以用traits实现类似效果
- 不需要显式try/catch,或者throws
- 不支持java8 的TYPE_PARAMETER, TYPE_USE