Groovy Document 4.0.14
官方文档:https://groovy-lang.org/documentation.html
下载安装
https://groovy.apache.org/download.html#distro ,下载binary,apache-groovy-binary-4.0.21.zip,解压,
配置GROOVY_HOME
,配置GROOVY_HOME/bin
到path
,配置JAVA_HOME
将bin目录配置到path,groovy -v
验证
jdk要求:jdk8-jdk16
与Java的区别
默认的imports
以下包会自动导入
java.io.*
java.lang.*
java.math.BigDecimal
java.math.BigInteger
java.net.*
java.util.*
groovy.lang.*
groovy.util.*
多方法 或者叫运行时分发
在Groovy中,方法调用基于参数的类型在运行时才确定。而java,与之相反,方法调用基于参数声明的类型,在编译时就确定了。
int method(String arg) {
return 1;
}
int method(Object arg) {
return 2;
}
Object o = "Object";
int result = method(o); //in java 2, in groovy 1
这是因为java使用了类型的静态信息,其中o
是Object
类型,Groovy在运行时根据参数的实际类型进行调用。
数组初始化
在java里,有两种初始化数组的方法:
int[] array = {1, 2, 3}; // Java array initializer shorthand syntax
int[] array2 = new int[] {4, 5, 6}; // Java array initializer long syntax
在Groovy中,{...}
保留给闭包使用的,所以不能使用上面第一种进行初始化,可以使用字面列表标记:
int[] array = [1, 2, 3]
在Groovy3+,支持了java长语法初始化
def array2 = new int[] {1, 2, 3} // Groovy 3.0+ supports the Java-style array initialization long syntax
包可见性
在Groovy中,忽略字段的修饰符并不会导致这个字段为包私有,而是为private,并且会生成对应的getter/setter方法
class Person {
String name
}
如果要创建包私有的字段,使用注解:
class Person {
@PackageScope String name
}
自动资源管理
java7增加了自动资源管理(try-with-resource)
Path file = Paths.get("/path/to/file");
Charset charset = Charset.forName("UTF-8");
try (BufferedReader reader = Files.newBufferedReader(file, charset)) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
Groovy3+支持了这样的写法,但是Groovy提供了其他各种各样更方便的闭包写法,可以达到统一的效果。
new File('/path/to/file').eachLine('UTF-8') {
println it
}
//或者 这种写法跟java更接近
new File('/path/to/file').withReader('UTF-8') { reader ->
reader.eachLine {
println it
}
}
内部类
匿名内部类和嵌套类与java相近,但是也有一些不同,比如在这样的类中访问局部变量不要求是final的,生成内部类字节码时,一些实现细节在groovy.lang.Closure
。
静态内部类是最好的支持,如果一定要使用内部类,就应该使用静态内部类
class A {
static class B {}
}
new A.B()
匿名内部类
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
CountDownLatch called = new CountDownLatch(1)
Timer timer = new Timer()
timer.schedule(new TimerTask() {
void run() {
called.countDown()
}
}, 0)
assert called.await(10, TimeUnit.SECONDS)
创建非静态内部类实例
java里面:
public class Y {
public class X {}
public X foo() {
return new X();
}
public static X createX(Y y) {
return y.new X();
}
}
在Groovy3之前不支持,必须使用new X(y)
。注意:Groovy支持调用有一个参数的方法但是不传递参数,这个参数值为null。调用构造器也是,那么,调用new X()
时调用了new X(this)
可能有问题,可能也是一个正常的调用,官方还没有好的方式防止你这样做
lambda表达式和方法引用操作符
java8+支持lambda表达式和方法引用操作符:
Runnable run = () -> System.out.println("Run"); // Java
list.forEach(System.out::println);
Groovy3+也支持,之前必须使用闭包:
Runnable run = { println 'run' }
list.each { println it } // or list.each(this.&println)
GStrings
双引号引用的字面量作为GString类型,有时带有$符号的String类型会产生编译错误。一般情况下Groovy会自动转换GString和String
String和字符字面量
在Groovy中,单引号引用的是String
,使用双引号引用的是String
或者GString
,取决于里面是否有插值:
assert 'c'.class == String
assert "c".class == String
assert "c${1}".class in GString
当赋值单引号引用的内容给char
类型时,会自动类型转换,但是调用方法时必须显式转换
char a = 'a' //自动类型转换String为char
assert Character.digit(a, 16) == 10: 'But Groovy does boxing'
assert Character.digit((char) 'a', 16) == 10 //强制类型转为char c-style
try {
assert Character.digit('a', 16) == 10 //必须显式转换
assert false: 'Need explicit cast'
} catch(MissingMethodException e) {
}
多个字符会使用第一个字符,C类型的转换则会失败
// for single char strings, both are the same
assert ((char) "c").class == Character //c-style强制类型转换
assert ("c" as char).class == Character //groovy类型转换
// for multi char strings they are not
try {
((char) 'cx') == 'c' //c-style类型转换会失败 无法转换
assert false: 'will fail - not castable'
} catch(GroovyCastException e) {
}
assert ('cx' as char) == 'c' //groovy类型转换更宽容 使用第一个字符
assert 'cx'.asType(char) == 'c' //groovy类型转换更宽容 使用第一个字符
==的行为
java里面==
指原生类型相等或者对象引用相等,在Groovy中,==
在所有的地方都有意义,对于非原生类型,会转换为a.compareTo(b)
,如果是Comparable
对象,会使用a.equals(b)
。
如果要判断引用是否相等,使用a.is(b)
,在Groovy3+,可以使用a === b
或者c !== d
原生类型与包装类型
java支持原生类型的自动装箱、拆箱,Groovy也支持,同时Groovy将所有的原生类型都在需要的时候进行自动装箱。
class Main {
float z1 = 0.0f
}
assert !(new Main().z1.equals(1.0f)) // groovy可以编译 java无法编译
java扩展类型的宽度优于类型装箱
int i
m(i)
void m(long l) { // java会调用这个方法
println "in m(long)"
}
void m(Integer i) { //groovy则调用这个方法
println "in m(Integer)"
}
使用@CompileStatic原生数据类型优化
因为groovy在大多数地方都把原生类型转换为包装类型,会产生低效率的字节码,可以使用@CompileStatic
,则使用原生类型表达式,会产生和java一样的字节码
正负零的边界情况
java里原生类型的正负零是相等的,但是包装类equals是不相等的。在groovy里,因为会自动转换,为了避免混淆,推荐遵循下面的做法:
- 如果期望区分正零和负零,使用equals,或者在使用
==
前将原生类型转换为包装类型 - 如果想忽略区别,直接使用
equalsIgnoreZeroSign
,或者在使用==
之前,将包装类型转为原生类型
约定
java可以自动扩展或者窄化数据类型,groovy进行了扩展:https://groovy-lang.org/differences.html#_conversions
其他的关键字
as
def
in
trait
it // within closures
Groovy没有java那么严格,可以在一些不允许出现关键字的地方使用,比如var var = [def: 1, as: 2, in: 3, trait: 4]
,但是不建议使用。
Groovy关键字:https://docs.groovy-lang.org/latest/html/documentation/core-syntax.html#_keywords
本文来自博客园,作者:Bingmous,转载请注明原文链接:https://www.cnblogs.com/bingmous/p/18141895