Groovy语法基础
2017-08-09 16:09
218 查看
Groovy与Java
Groovy是一种与Java非常相似的脚本语言,编译器会将该脚本语言编译成class字节码文件,最终运行于Java虚拟机之上。环境配置
前提是配置好JDKGroovy环境在类Unix上配置,只需以下的几行命令即可:
第一步下载sdkman,这是管理sdk的工具,命令如下:
curl -s get.sdkman.io | bash
读取并执行sdkman的初始化脚本,如下命令:
source "$HOME/.sdkman/bin/sdkman-init.sh"
接着安装groovy的sdk,命令如下:
sdk install groovy
最后,检验是否安装成功:
groovy -version
Groovy工具
安装完Groovy环境之后,输入groovy命令时,会有如下的工具:groovyc:groovy编译器,类似于javac,将groovy脚本编译成class字节码文件
groovy:用于运行groovy脚本
groovyConsole:Groovy官方提供的一个简易IDE,如下图
groovysh:groovy命令交互式的shell,类似python中的交互式环境。
另外,很多出名的IDE均已支持Groovy,本人使用的是IntelliJ IDEA。
引用标识符
注意,Groovy的语法与Java十分相似,这里只重点介绍与Java有区别的语法,相同的就不再赘述。引用标识符:Groovy中对变量的引用方式是多样化的,Test.groovy源码如下:
void testQuotedIdentifiers() { def map = [:] map.no_quote = 1 map.'single quote' = 2 map."double quote" = 3 map.'''triple single quote''' = 4 119f1 map."""triple double quote""" = 5 map./slashy string/ = 6 map.$/dollar slashy string/$ = 7 def closureQuote = "value" map."closure quote ${closureQuote}" = 8 println(map) } testQuotedIdentifiers()
运行结果如下:
[no_quote:1, single quote:2, double quote:3, triple single quote:4, triple double quote:5, slashy string:6, dollar slashy string:7, closure quote value:8]
上述代码先定义一个静态方法,然后在方法内定义了一个Map(本质上是java中的LinkedHashMap实例)。接着使用不同的引用操作符的方式往这个map中添加键值对,最后打印这个map实例,如下图:
紧接着来看Test.groovy编译之后的class代码:
public class Test extends Script { public Test() { CallSite[] var1 = $getCallSiteArray(); } public Test(Binding context) { CallSite[] var2 = $getCallSiteArray(); super(context); } public static void main(String... args) { CallSite[] var1 = $getCallSiteArray(); var1[0].call(InvokerHelper.class, Test.class, args); } public Object run() { CallSite[] var1 = $getCallSiteArray(); if (!__$stMC && !BytecodeInterface8.disabledStandardMetaClass()) { this.testQuotedIdentifiers(); return null; } else { return var1[1].callCurrent(this); } } public void testQuotedIdentifiers() { CallSite[] var1 = $getCallSiteArray(); Object map = ScriptBytecodeAdapter.createMap(new Object[0]); byte var3 = 1; ScriptBytecodeAdapter.setProperty(Integer.valueOf(var3), (Class)null, map, (String)"no_quote"); byte var4 = 2; ScriptBytecodeAdapter.setProperty(Integer.valueOf(var4), (Class)null, map, (String)"single quote"); byte var5 = 3; ScriptBytecodeAdapter.setProperty(Integer.valueOf(var5), (Class)null, map, (String)"double quote"); byte var6 = 4; ScriptBytecodeAdapter.setProperty(Integer.valueOf(var6), (Class)null, map, (String)"triple single quote"); byte var7 = 5; ScriptBytecodeAdapter.setProperty(Integer.valueOf(var7), (Class)null, map, (String)"triple double quote"); byte var8 = 6; ScriptBytecodeAdapter.setProperty(Integer.valueOf(var8), (Class)null, map, (String)"slashy string"); byte var9 = 7; ScriptBytecodeAdapter.setProperty(Integer.valueOf(var9), (Class)null, map, (String)"dollar slashy string"); Object closureQuote = "value"; byte var11 = 8; ScriptBytecodeAdapter.setProperty(Integer.valueOf(var11), (Class)null, map, (String)ShortTypeHandling.castToString(new GStringImpl(new Object[]{closureQuote}, new String[]{"closure quote ", ""}))); var1[2].callCurrent(this, map); } }
由上可知,Test.groovy编译成Test.class之后,会继承Script类,然后定义了两个构造方法、main方法,run方法,最后才是testQuotedIdentifiers方法。
在testQuotedIdentifiers方法中调用了ScriptBytecodeAdapter.createMap方法创建了map实例,代码如下:
public static Map createMap(Object[] values) { return InvokerHelper.createMap(values); }
上述的方法只是一个封装,具体实现在InvokerHelper中:
public static Map createMap(Object[] values) { Map answer = new LinkedHashMap(values.length / 2); int i = 0; while (i < values.length - 1) { if ((values[i] instanceof SpreadMap) && (values[i + 1] instanceof Map)) { Map smap = (Map) values[i + 1]; Iterator iter = smap.keySet().iterator(); for (; iter.hasNext();) { Object key = iter.next(); answer.put(key, smap.get(key)); } i += 2; } else { answer.put(values[i++], values[i++]); } } return answer; }
上述代码因为参数传递过来的是new Object[0],所以是直接new了一个LinkedHashMap实例就返回了。回到testQuotedIdentifiers中可以看到其实前面7种引用方式本质是一样,只有闭包引用方式有区别,但是最终都是ScriptBytecodeAdapter的setProperty方法完成map的添加操作。
字符串
Groovy字符串分为好种:单引号字符串、双引号字符串、三引号字符串等,这些字符串均对应不同的应用场景。单引号字符串:本质上是java的String,与java的String的使用方法也是一样。如下代码:
void testSingleQuotedString() { def ss = 'SingleQuotedString' println(ss) } testSingleQuotedString()
上述定义了一个单引号字符串然后打印到控制台。再来看看编译过后的class代码:
public void testSingleQuotedString() { CallSite[] var1 = $getCallSiteArray(); Object ss = "SingleQuotedString"; var1[3].callCurrent(this, ss); }
单引号字符串(包括双引号字符串)的限制是只能单行来使用,这与Groovy的语法特性有关。
三引号字符串:分为三单引号字符串和三双引号字符串,目的是为了解决单行限制,如下三单引号字符串代码:
void testTripleSingleQuotedString() { def tss = '''line one line two line three''' println(tss) } testTripleSingleQuotedString()
再看编译过后的class代码,原来Groovy中多行字符串的实现原理是加了\n换行符,其实现也是java的String对象:
public void testTripleSingleQuotedString() { CallSite[] var1 = $getCallSiteArray(); Object tss = "line one \nline two\nline three"; var1[6].callCurrent(this, tss); }
双引号字符串:如果字符串中没有插值符,其底层实现是java的String,否则是groovy.lang.GString,如下实例:
void testDoubleQuotedString() { def person = [:] person.'name' = 'Jack' def one = "person name:" + person.get("name") def two = "person name:$person.name " print(one + ' and ' + two) } testDoubleQuotedString()
上述代码定义了一个Map,在第一句println中是双引号字符串的普通使用,下一句是使用插值符用法。再来看看,编译后的class源码:
public void testDoubleQuotedString() { CallSite[] var1 = $getCallSiteArray(); Object person = ScriptBytecodeAdapter.createMap(new Object[0]); String var3 = "Jack"; ScriptBytecodeAdapter.setProperty(var3, (Class)null, person, (String)"name"); //双引号字符串的普通用法 Object one = var1[2].call("person name:", var1[3].call(person, "name")); //双引号字符串的插值符用法 Object two = new GStringImpl(new Object[]{var1[4].callGetProperty(person)}, new String[]{"person name:", " "}); var1[5].callCurrent(this, var1[6].call(var1[7].call(one, " and "), two)); }
由上可知双引号字符串普通用法实现是Java的String,而插值符用法则使用了GStringImpl。
双引号字符串的插值符合+闭包表达式,展现了Groovy语言强大的一面,如下代码:
void testInterpolatingClosureExpressions() { def number = 1 def eagerGString = "value == ${number}" def lazyGString = "value == ${-> number}" println(eagerGString) println(lazyGString) number = 2 println(eagerGString) println(lazyGString) } testInterpolatingClosureExpressions()
上述的编译后的class如下:
public void testInterpolatingClosureExpressions() { CallSite[] var1 = $getCallSiteArray(); Reference number = new Reference(Integer.valueOf(1)); Object eagerGString = new GStringImpl(new Object[]{number.get()}, new String[]{"value == ", ""}); class _testInterpolatingClosureExpressions_closure1 extends Closure implements GeneratedClosure { public _testInterpolatingClosureExpressions_closure1(Object _thisObject, Reference number) { CallSite[] var4 = $getCallSiteArray(); super(Test.this, _thisObject); this.number = number; } public Object doCall() { CallSite[] var1 = $getCallSiteArray(); return this.number.get(); } public Object getNumber() { CallSite[] var1 = $getCallSiteArray(); return this.number.get(); } } Object lazyGString = new GStringImpl(new Object[]{new _testInterpolatingClosureExpressions_closure1(this, number)}, new String[]{"value == ", ""}); var1[2].callCurrent(this, eagerGString); var1[3].callCurrent(this, lazyGString); byte var5 = 2; ((Reference)number).set(Integer.valueOf(var5)); var1[4].callCurrent(this, eagerGString); var1[5].callCurrent(this, lazyGString); }
区别是构造GStringImpl的参数不同,eagerGString使用的是Reference类型,而lazyGString使用的是_testInterpolatingClosureExpressions_closure1。
Slashy字符串:即斜杠字符串,是为正则表达式设计的,原因是除了正斜杠需要转义,其它的均不再需要转义。它还支持多行、插值的特性,如下代码:
void testSlashyString() { def fooPattern = /.*foo.*/ println(fooPattern) def escapeSlash = /The character \/ is a forward slash/ println(escapeSlash) def multilineSlashy = /one two three/ println(multilineSlashy) def color = 'blue' def interpolatedSlashy = /a ${color} car/ println(interpolatedSlashy) } testSlashyString()
再看编译过后的class代码,会发现与双引号字符串本质是一样的:
public void testSlashyString() { CallSite[] var1 = $getCallSiteArray(); Object fooPattern = ".*foo.*"; var1[2].callCurrent(this, fooPattern); Object escapeSlash = "The character / is a forward slash"; var1[3].callCurrent(this, escapeSlash); Object multilineSlashy = "one\n two\n three"; var1[4].callCurrent(this, multilineSlashy); Object color = "blue"; Object interpolatedSlashy = new GStringImpl(new Object[]{color}, new String[]{"a ", " car"}); var1[5].callCurrent(this, interpolatedSlashy); }
Groovy中字符类型要借助于字符串来辅助完成创建,方法如下:
void testCharacters() { char c1 = 'A' assert c1 instanceof Character def c2 = 'B' as char assert c2 instanceof Character def c3 = (char) 'C' assert c3 instanceof Character } testCharacters()
数据类型
整型:Groovy中的byte、char、short、int、long和java.lang.BigInteger均跟Java一样,如下示例:void testIntegralLiterals() { // primitive types byte b = 1 char c = 2 short s = 3 int i = 4 long l = 5 // infinite precision BigInteger bi = 6 }
使用def来定义来整型数据,编译器会自动适配数据的类型,原则是在不丢失数据的前提下选取小容量类型,如下例子:
void testAdaptIntegralType() { 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 }
除了十进制,Groovy同样支持二进制、八进制与十六进制,使用如下:
void test() { //二进制,以0b开头 int xInt = 0b10101111 assert xInt == 175 //八进制,以0开头 int xInt = 077 assert xInt == 63 //十六进制,以0x开头 int xInt = 0x77 assert xInt == 119 }
Lists类型
Lists:Groovy使用方括号来定义列表,如下示例:void testLists() { def numbers = [1, 2, 3] println('number:' + numbers.size()) }
上述代码编译过的字节码如下:
public void testLists() { CallSite[] var1 = $getCallSiteArray(); Object numbers = ScriptBytecodeAdapter.createList(new Object[]{Integer.valueOf(1), Integer.valueOf(2), Integer.valueOf(3)}); var1[2].callCurrent(this, var1[3].call("number:", var1[4].call(numbers))); }
通过ScriptBytecodeAdapter的createList创建了这个list,再看其源码:
public static List createList(Object[] values) { return InvokerHelper.createList(values); }
InvokerHelper中的createList方法
public static List createList(Object[] values) { List answer = new ArrayList(values.length); answer.addAll(Arrays.asList(values)); return answer; }
由上可知,Groovy的list类型就是java的list类型,而且默认使用的ArrayList类型。
一个list可存放多种类型数据,:
void testDiffType() { def list = [1,'abc',true] println(list.size()) }
上述的list默认都是ArrayList类型,可通过as标识符或明确指定类型来修改:
void test() { def linkedList = [2, 3, 4] as LinkedList assert linkedList instanceof java.util.LinkedList LinkedList otherLinked = [3, 4, 5] assert otherLinked instanceof java.util.LinkedList }
list元素:与一般的脚本语言类似,可使用正或负下标来访问元素,通过<<符合往列表尾部添加元素,具体如下:
void testElementsOperation() { 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'] }
Arrays类型
Groovy的数组用法除了需要显式定义,其余用法与list一样,如下示例:void testArrays() { // 直接定义为数组 String[] arrStr = ['one', 'second', '3'] println('arrStr len:' + arrStr.length) // 使用as标识符指定为数组 def numArr = [1, 2, 3] as int[] println('numArr len:' + numArr.length) }
上述代码的字节码如下:
public void testArrays() { CallSite[] var1 = $getCallSiteArray(); String[] arrStr = new String[]{"one", "second", "3"}; var1[2].callCurrent(this, var1[3].call("arrStr len:", var1[4].callGetProperty(arrStr))); Object numArr = (int[])ScriptBytecodeAdapter.asType(ScriptBytecodeAdapter.createList(new Object[]{Integer.valueOf(1), Integer.valueOf(2), Integer.valueOf(3)}), int[].class); var1[5].callCurrent(this, var1[6].call("numArr len:", var1[7].callGetProperty(numArr))); }
上面as标识符代码:将ScriptBytecodeAdapter.createList创建出来的ArrayList与int[].class作为ScriptBytecodeAdapter.asType的参数,最终返回了int数组。
Map类型
Groovy中Map的key是不限类型,基本用法如下:void testMap() { def colors = [red: '#FF0000', green: '#00FF00', blue: '#0000FF'] colors['pink'] = '#FF00FF' colors.yellow = '#FFFF00' println('size:' + colors.size() + ",blue:" + colors.blue) }
当要获取Map的key时,需定义key这个变量,且定义map时添加圆括号,如下:
void testMapKey() { def key = 'name' def person = [key: 'Guillaume'] println("key:" + person.name) person = [(key): 'Guillaume'] println("key:" + person.name) } 结果: key:null key:Guillaume
上述key不加圆括号为null的原因是:person直接将key这字符串当作了map的key而不是将key的内容name作为key。
相关文章推荐
- Groovy的基础语法
- Groovy的基础语法
- Gradle基础之Groovy语法
- groovy语法基础
- gradle学习(12)-groovy一些基础语法
- Groovy的基础语法
- Groovy基础语法
- Android:Groovy基础语法
- Groovy基础语法总结
- Groovy的基础语法
- Groovy语言语法基础
- Groovy的基础语法
- 块语法block 在iOS4中使用代码块-基础知识(翻译)
- 第二章:Java语言的基础语法
- Python语法基础21.函数(四)
- 学习java基础语法
- cocos2d JS-(JavaScript) 基础语法间的函数方法相互调用
- 黑马程序员 Java基础语法——上
- 【php学习之路】php基础语法