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
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?