我非常喜欢Groovy编程语言。我喜欢它是因为,归根结底,我喜欢Java,尽管Java有时感觉很笨拙。因为我非常喜欢Java,所以我不觉得许多其他的JVM语言特别有吸引力。例如,Kotlin、Scala和Clojure,感觉和Java不太一样,它们对什么是好的编程语言追求自己的观点。Groovy则不同;在我看来,当喜欢Java的程序员只是需要一些更灵活、更紧凑、有时甚至更直接的东西时,Groovy是那些情况的完美解药。
一个很好的例子是List数据结构,它被用来保存数字、字符串或对象的有序列表,并允许程序员以有效的方式迭代这些项目。特别是对于编写和维护脚本的人来说,"效率 "主要是指清晰而简短的表达,不需要一堆掩盖代码意图的仪式。
安装Java和Groovy
Groovy是基于Java的,也需要安装Java。最近的、像样的Java和Groovy版本可能都在你的Linux发行版的软件库中。否则,你可以按照这些说明来安装Groovy。对于Linux用户来说,一个不错的选择是SDKMan,它可以用来获取多个版本的Java、Groovy和许多其他相关工具。在这篇文章中,我使用了SDK的以下版本。
- Java:OpenJDK 11的11.0.12-open版本
- Groovy:版本3.0.8
回到问题上来
自从Java中首次引入列表以来(我想那是Java 1.5,但请不要引用我的话),一直有各种实例化和初始化列表的方法。目前有两种有趣的方式涉及两个不同的库:java.util.Arrays和java.util.List。
使用java.util.Arrays
java.util.Arrays定义了静态方法asList(),它可以用来创建一个由数组支持的列表,因此也是不可变的,尽管其元素是可变的。下面是它的操作。
var a1 = Arrays.asList(1,2,3,4,5,6,7,8,9,10); // immutable list of mutable elements
System.out.println("a1 = " + a1);
System.out.println("a1 is an instance of " + a1.getClass());
// output is
// a1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
// a1 is an instance of class java.util.Arrays$ArrayList
a1.set(0,0); // succeeds
System.out.println("a1 = " + a1); // output is
// a1 = [0, 2, 3, 4, 5, 6, 7, 8, 9, 10]
a1.add(11); // fails producing
// Exception in thread "main" java.lang.UnsupportedOperationException
System.out.println("a1 = " + a1); // not reached
使用 java.util.List
java.util.List定义了静态方法**of()。**这可以用来创建一个不可变的列表,其元素可能是不可变的,也可能不是,这取决于元素列表中的项目是否是不可变的。下面是这个版本的操作。
var a2 = List.of(1,2,3,4,5,6,7,8,9,10);
System.out.println("a2 = " + a2);
System.out.println("a2 is an instance of " + a2.getClass());
// output is
// a2 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
// a2 is an instance of class java.util.ImmutableCollections$ListN
a2.set(0,0); // fails producing
// Exception in thread "main" java.lang.UnsupportedOperationException
System.out.println("a2 = " + a2); // not reached
a2.add(11); // also fails for same reason if above two lines commented out
System.out.println("a2 = " + a2); // not reached
所以,如果我想要一个不能被增长(或缩小)的列表,并且可能有也可能没有可改变的元素,我可以使用Arrays.asList()或List.of()。
如果我想要一个初始化的可改变的列表,我可能会求助于使用那些不可改变的列表作为列表构造函数的参数,例如。
var a1 = new ArrayList(Arrays.asList(1,2,3,4,5,6,7,8,9,10));
System.out.println("a1 = " + a1);
System.out.println("a1 is an instance of " + a1.getClass());
// output is
// a1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
// a1 is an instance of class java.util.ArrayList
a1.set(0,0);
System.out.println("a1 = " + a1);
//output is
// a1 = [0, 2, 3, 4, 5, 6, 7, 8, 9, 10]
a1.add(11);
System.out.println("a1 = " + a1);
// output is
// a1 = [0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
注意,Arrays.AsList()被用来初始化新的ArrayList(),它创建了一个参数的可变副本。
现在也许只是我的问题,但这似乎是一个非常多的理论--需要根据情况了解java.util.Arrays或java.util.List的细节--只是为了创建和初始化一个可变的整数列表,尽管实际使用的语句并不过分 "礼仪"。这里再介绍一下,供参考。
var a1 = new ArrayList(Arrays.asList(1,2,3,4,5,6,7,8,9,10));
Groovy方法
下面是上述内容的Groovy版本。
def a1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
println "a1 = $a1"
println "a1 is an instance of ${a1.getClass()}"
// output is
// a1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
// a1 is an instance of class java.util.ArrayList
a1[0] = 0
println "a1 = $a1"
// output is
// a1 = [0, 2, 3, 4, 5, 6, 7, 8, 9, 10]
a1 << 11
println "a1 = $a1"
// output is
// a1 = [0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
我还知道,我可以通过把一列东西--本例中是整数--放在括号里来创建一个列表表示。此外,这样创建的列表实例正是我想要的:ArrayList 的可变实例。
现在也许又是我的问题,但上面的方法似乎简单了许多--不用记住由**.of()或.asList()返回的半不可变的结果,也不用为它们做补偿。同样不错的是,我可以用大括号指代列表中的某个特定元素,而不是用方法调用set(),而且<< 操作符可以追加到列表的末尾,这样我就不用使用方法调用add()了。另外,你注意到没有分号吗?是的,在Groovy中,它们是可选的。最后,观察一下字符串插值的使用,双引号字符串中的{expression}**提供了这种能力。
在Groovy的世界里,还有更多的事情 "隐藏 "着。这个定义是动态类型(Groovy的默认类型)与Java的静态类型的一个例子。在Groovy的定义行中,a1的类型是在运行时从右侧评估的表达式的类型中推断出来的。现在我们都知道,动态编程语言给了我们很大的权力,而有了很大的权力,就会有很多搞砸的好机会。但是对于那些不喜欢动态类型的程序员,Groovy提供了静态类型的选择。
Groovy资源
我在开头提到的Apache Groovy网站有很多很好的文档。另一个优秀的Groovy资源是Haki先生。而学习Groovy的一个真正好的理由是继续学习Grails,它是一个非常有成效的全栈网络框架,建立在Hibernate、Spring Boot和Micronaut等优秀组件之上。
这篇文章献给我非常亲爱的朋友Anil Mukhi,他在2022年1月3日去世了。谢谢你,Anil,让我有机会学习到这么多关于Groovy、Grails和赛马数据的知识。